mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
merge main
This commit is contained in:
commit
29561372de
126 changed files with 3728 additions and 2429 deletions
|
@ -7,6 +7,11 @@ use std::path::{Path, PathBuf};
|
|||
use std::{fmt, io};
|
||||
use ven_pretty::{text, BoxAllocator, DocAllocator, DocBuilder, Render, RenderAnnotated};
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use byte_unit::Byte;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use roc_packaging::https::Problem;
|
||||
|
||||
pub use crate::error::canonicalize::can_problem;
|
||||
pub use crate::error::parse::parse_problem;
|
||||
pub use crate::error::r#type::type_problem;
|
||||
|
@ -1075,6 +1080,471 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
pub fn to_https_problem_report_string(url: &str, https_problem: Problem) -> String {
|
||||
let src_lines: Vec<&str> = Vec::new();
|
||||
|
||||
let mut module_ids = ModuleIds::default();
|
||||
|
||||
let module_id = module_ids.get_or_insert(&"find module name somehow?".into());
|
||||
|
||||
let interns = Interns::default();
|
||||
|
||||
// Report parsing and canonicalization problems
|
||||
let alloc = RocDocAllocator::new(&src_lines, module_id, &interns);
|
||||
|
||||
let mut buf = String::new();
|
||||
let palette = DEFAULT_PALETTE;
|
||||
let report = to_https_problem_report(&alloc, url, https_problem);
|
||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||
|
||||
buf
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
pub fn to_https_problem_report<'b>(
|
||||
alloc: &'b RocDocAllocator<'b>,
|
||||
url: &'b str,
|
||||
https_problem: Problem,
|
||||
) -> Report<'b> {
|
||||
match https_problem {
|
||||
Problem::UnsupportedEncoding(not_supported_encoding) => {
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I was trying to download this URL:"),
|
||||
alloc.string((&url).to_string()).annotate(Annotation::Url).indent(4),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"But the server replied with a "),
|
||||
alloc.reflow(r"content encoding").annotate(Annotation::Emphasized),
|
||||
alloc.reflow(r" that I do not understand ("),
|
||||
alloc.string(not_supported_encoding).annotate(Annotation::Emphasized),
|
||||
alloc.reflow(r")."),
|
||||
]),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"The supported content encodings are "),
|
||||
alloc.keyword(r"br"),
|
||||
alloc.reflow(r", "),
|
||||
alloc.keyword(r"gzip"),
|
||||
alloc.reflow(r" and "),
|
||||
alloc.keyword(r"deflate"),
|
||||
]),
|
||||
alloc.concat([
|
||||
alloc.tip(),
|
||||
alloc.reflow(r"Perhaps you can check if the URL is correctly formed, or if the server is correctly configured."),
|
||||
]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "UNSUPPORTED ENCODING".to_string(),
|
||||
severity: Severity::Fatal,
|
||||
}
|
||||
}
|
||||
Problem::MultipleEncodings(multiple_encodings) => {
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I was trying to download this URL:"),
|
||||
alloc.string((&url).to_string()).annotate(Annotation::Url).indent(4),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"But the server replied with multiple "),
|
||||
alloc.reflow(r"content encodings").annotate(Annotation::Emphasized),
|
||||
alloc.reflow(r": "),
|
||||
alloc.string(multiple_encodings).annotate(Annotation::Emphasized),
|
||||
alloc.reflow(r"."),
|
||||
]),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"The supported content encodings are "),
|
||||
alloc.keyword(r"br"),
|
||||
alloc.reflow(r", "),
|
||||
alloc.keyword(r"gzip"),
|
||||
alloc.reflow(r" and "),
|
||||
alloc.keyword(r"deflate"),
|
||||
alloc.reflow(r". However, the server reply can only contain "),
|
||||
alloc.reflow(r"one").annotate(Annotation::Emphasized),
|
||||
alloc.reflow(r"."),
|
||||
]),
|
||||
alloc.concat([
|
||||
alloc.tip(),
|
||||
alloc.reflow(r"Perhaps you can check if the URL is correctly formed, or if the server is correctly configured."),
|
||||
]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "MULTIPLE ENCODINGS".to_string(),
|
||||
severity: Severity::Fatal,
|
||||
}
|
||||
}
|
||||
Problem::InvalidContentHash { expected, actual } => {
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I was able to download this URL:"),
|
||||
alloc.string((&url).to_string()).annotate(Annotation::Url).indent(4),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"I use a mechanism to detect if the file might "),
|
||||
alloc.reflow(r"have been tampered with. This could happen if "),
|
||||
alloc.reflow(r"the server or domain have been compromised."),
|
||||
]),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"This is the content signature I was "),
|
||||
alloc.reflow(r"expecting").annotate(Annotation::Emphasized),
|
||||
alloc.reflow(r":"),
|
||||
]),
|
||||
alloc.string(expected).annotate(Annotation::PlainText).indent(4),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"However, this is the content signature I "),
|
||||
alloc.reflow(r"obtained").annotate(Annotation::Emphasized),
|
||||
alloc.reflow(r":"),
|
||||
]),
|
||||
alloc.string(actual).annotate(Annotation::PlainText).indent(4),
|
||||
alloc.reflow(r"To keep you secure, I will not execute this untrusted code."),
|
||||
alloc.concat([
|
||||
alloc.tip(),
|
||||
alloc.reflow(r"Check if the URL is correctly formed and if this is the server you are expecting to connect to."),
|
||||
]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "INVALID CONTENT HASH".to_string(),
|
||||
severity: Severity::Fatal,
|
||||
}
|
||||
}
|
||||
// TODO: The reporting text for IoErr and FsExtraErr could probably be unified
|
||||
Problem::IoErr(io_error) => {
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I was trying to download this URL:"),
|
||||
alloc
|
||||
.string((&url).to_string())
|
||||
.annotate(Annotation::Url)
|
||||
.indent(4),
|
||||
alloc.reflow(r"But I encountered an IO (input/output) error:"),
|
||||
alloc
|
||||
.string(io_error.to_string())
|
||||
.annotate(Annotation::PlainText)
|
||||
.indent(4),
|
||||
// TODO: What should the tip for IO errors be?
|
||||
// alloc.concat([
|
||||
// alloc.tip(),
|
||||
// alloc.reflow(r"Check the error message."),
|
||||
// ]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "IO ERROR".to_string(),
|
||||
severity: Severity::Fatal,
|
||||
}
|
||||
}
|
||||
// TODO: The reporting text for IoErr and FsExtraErr could probably be unified
|
||||
Problem::FsExtraErr(fs_extra_error) => {
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I was trying to download this URL:"),
|
||||
alloc
|
||||
.string((&url).to_string())
|
||||
.annotate(Annotation::Url)
|
||||
.indent(4),
|
||||
alloc.reflow(r"But I encountered an IO (input/output) error:"),
|
||||
alloc
|
||||
.string(fs_extra_error.to_string())
|
||||
.annotate(Annotation::PlainText)
|
||||
.indent(4),
|
||||
// TODO: What should the tip for IO errors be?
|
||||
// alloc.concat([
|
||||
// alloc.tip(),
|
||||
// alloc.reflow(r"Check the error message."),
|
||||
// ]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "IO ERROR".to_string(),
|
||||
severity: Severity::Fatal,
|
||||
}
|
||||
}
|
||||
Problem::HttpErr(reqwest_error) => {
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I was trying to download this URL:"),
|
||||
alloc
|
||||
.string((&url).to_string())
|
||||
.annotate(Annotation::Url)
|
||||
.indent(4),
|
||||
alloc.reflow(r"But I encountered a network error:"),
|
||||
alloc
|
||||
.string(reqwest_error.to_string())
|
||||
.annotate(Annotation::PlainText)
|
||||
.indent(4),
|
||||
// TODO: What should the tip for HTTP IO errors be?
|
||||
// Should we import reqwest and check stuff like
|
||||
// reqwest_error.{ is_redirect(), is_status(), is_timeout(), ... } ?
|
||||
//
|
||||
// alloc.concat([
|
||||
// alloc.tip(),
|
||||
// alloc.reflow(r"Check the error message."),
|
||||
// ]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "HTTP ERROR".to_string(),
|
||||
severity: Severity::Fatal,
|
||||
}
|
||||
}
|
||||
Problem::InvalidUrl(roc_packaging::https::UrlProblem::InvalidExtensionSuffix(
|
||||
invalid_suffix,
|
||||
)) => {
|
||||
let (suffix_text, annotation_style) = if invalid_suffix.is_empty() {
|
||||
(r"empty".to_string(), Annotation::PlainText)
|
||||
} else {
|
||||
(invalid_suffix, Annotation::Emphasized)
|
||||
};
|
||||
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I was trying to download this URL:"),
|
||||
alloc
|
||||
.string((&url).to_string())
|
||||
.annotate(Annotation::Url)
|
||||
.indent(4),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"However, this file's extension ("),
|
||||
alloc.string(suffix_text).annotate(annotation_style),
|
||||
alloc.reflow(r") is not a supported extension."),
|
||||
]),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"The supported extensions are "),
|
||||
alloc.keyword(r".tar"),
|
||||
alloc.reflow(r", "),
|
||||
alloc.keyword(r".tar.gz"),
|
||||
alloc.reflow(r" and "),
|
||||
alloc.keyword(r".tar.br"),
|
||||
]),
|
||||
alloc.concat([
|
||||
alloc.tip(),
|
||||
alloc.reflow(r"Check that you have the correct URL for this package/platform."),
|
||||
]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "INVALID EXTENSION SUFFIX".to_string(),
|
||||
severity: Severity::Fatal,
|
||||
}
|
||||
}
|
||||
Problem::InvalidUrl(roc_packaging::https::UrlProblem::MissingTarExt) => {
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I was trying to download this URL:"),
|
||||
alloc
|
||||
.string((&url).to_string())
|
||||
.annotate(Annotation::Url)
|
||||
.indent(4),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"However, this file's extension is not "),
|
||||
alloc.keyword(r".tar"),
|
||||
alloc.reflow(r"."),
|
||||
]),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"The supported extensions are "),
|
||||
alloc.keyword(r".tar"),
|
||||
alloc.reflow(r", "),
|
||||
alloc.keyword(r".tar.gz"),
|
||||
alloc.reflow(r" and "),
|
||||
alloc.keyword(r".tar.br"),
|
||||
]),
|
||||
alloc.concat([
|
||||
alloc.tip(),
|
||||
alloc.reflow(r"Check that you have the correct URL for this package/platform."),
|
||||
]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "INVALID EXTENSION".to_string(),
|
||||
severity: Severity::Fatal,
|
||||
}
|
||||
}
|
||||
Problem::InvalidUrl(roc_packaging::https::UrlProblem::InvalidFragment(
|
||||
invalid_fragment,
|
||||
)) => {
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I was trying to download this URL:"),
|
||||
alloc
|
||||
.string((&url).to_string())
|
||||
.annotate(Annotation::Url)
|
||||
.indent(4),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"However, this URL's fragment (the part after #) "),
|
||||
alloc.reflow(r"is not valid. When present, the fragment must point to "),
|
||||
alloc.reflow(r"an existing "),
|
||||
alloc.keyword(r".roc"),
|
||||
alloc.reflow(r" file inside the package. Also, the filename can't be empty, "),
|
||||
alloc.reflow(r"so a fragment of #.roc would also not be valid. This is the "),
|
||||
alloc.reflow(r"invalid fragment I encountered: "),
|
||||
]),
|
||||
alloc
|
||||
.string(invalid_fragment)
|
||||
.annotate(Annotation::Emphasized)
|
||||
.indent(4),
|
||||
alloc.concat([
|
||||
alloc.tip(),
|
||||
alloc.reflow(r"Check that the fragment points to an existing "),
|
||||
alloc.keyword(r".roc"),
|
||||
alloc.reflow(r" file inside the package. You can download this package "),
|
||||
alloc.reflow(r"and inspect it locally."),
|
||||
]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "INVALID FRAGMENT".to_string(),
|
||||
severity: Severity::Fatal,
|
||||
}
|
||||
}
|
||||
Problem::InvalidUrl(roc_packaging::https::UrlProblem::MissingHash) => {
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I was trying to download this URL:"),
|
||||
alloc
|
||||
.string((&url).to_string())
|
||||
.annotate(Annotation::Url)
|
||||
.indent(4),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"I use a content hash to detect if the file might "),
|
||||
alloc.reflow(r"have been tampered with. This could happen if "),
|
||||
alloc.reflow(r"the server or domain have been compromised."),
|
||||
]),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"The way this works is that the name of the file "),
|
||||
alloc.reflow(r"is the BLAKE3 hash of the contents of the "),
|
||||
alloc.reflow(r"file itself. If someone would tamper with the file, "),
|
||||
alloc.reflow(r"I could notify and protect you. However, I could "),
|
||||
alloc.reflow(r"not find the expected hash on the URL above, "),
|
||||
alloc.reflow(r"so I cannot apply this tamper-check."),
|
||||
]),
|
||||
alloc.concat([
|
||||
alloc.tip(),
|
||||
alloc
|
||||
.reflow(r"Check that you have the correct URL for this package/platform. "),
|
||||
alloc.reflow(r"Here is an example of how such a hash looks like: "),
|
||||
alloc
|
||||
.string(r"tE4xS_zLdmmxmHwHih9kHWQ7fsXtJr7W7h3425-eZFk".to_string())
|
||||
.annotate(Annotation::Emphasized),
|
||||
]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "MISSING PACKAGE HASH".to_string(),
|
||||
severity: Severity::Fatal,
|
||||
}
|
||||
}
|
||||
Problem::InvalidUrl(roc_packaging::https::UrlProblem::MissingHttps) => {
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I was trying to download this URL:"),
|
||||
alloc
|
||||
.string((&url).to_string())
|
||||
.annotate(Annotation::Url)
|
||||
.indent(4),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"For your security, I will only attempt to download "),
|
||||
alloc.reflow(r"files from servers which use the "),
|
||||
alloc.keyword(r"https"),
|
||||
alloc.reflow(r" protocol."),
|
||||
]),
|
||||
alloc.concat([
|
||||
alloc.tip(),
|
||||
alloc.reflow(r"Check that you have the correct URL for this package/platform."),
|
||||
]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "HTTPS MANDATORY".to_string(),
|
||||
severity: Severity::Fatal,
|
||||
}
|
||||
}
|
||||
Problem::InvalidUrl(roc_packaging::https::UrlProblem::MisleadingCharacter) => {
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I was trying to download this URL:"),
|
||||
alloc
|
||||
.string((&url).to_string())
|
||||
.annotate(Annotation::Url)
|
||||
.indent(4),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"I have found one or more potentially misleading "),
|
||||
alloc.reflow(r"characters in this URL. Misleading characters are "),
|
||||
alloc.reflow(r"characters that look like others but aren't the same. "),
|
||||
alloc.reflow(r"The following characters are classified as misleading: "),
|
||||
alloc.keyword(r"@"),
|
||||
alloc.reflow(r", "),
|
||||
alloc.keyword("\u{2044}"),
|
||||
alloc.reflow(r" (unicode 2044), "),
|
||||
alloc.keyword("\u{2215}"),
|
||||
alloc.reflow(r" (unicode 2215), "),
|
||||
alloc.keyword("\u{FF0F}"),
|
||||
alloc.reflow(r" (unicode FF0F) and "),
|
||||
alloc.keyword("\u{29F8}"),
|
||||
alloc.reflow(r" (unicode 29F8). "),
|
||||
]),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"If you have a use-case for any of these characters we "),
|
||||
alloc.reflow(r"would like to hear about it. Reach out on "),
|
||||
alloc
|
||||
.string(r"https://github.com/roc-lang/roc/issues/5487".to_string())
|
||||
.annotate(Annotation::Url),
|
||||
]),
|
||||
alloc.concat([
|
||||
alloc.tip(),
|
||||
alloc.reflow(r"Check that you have the correct URL for this package/platform."),
|
||||
]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "MISLEADING CHARACTERS".to_string(),
|
||||
severity: Severity::Fatal,
|
||||
}
|
||||
}
|
||||
Problem::DownloadTooBig(content_len) => {
|
||||
let nice_bytes = Byte::from_bytes(content_len.into())
|
||||
.get_appropriate_unit(false)
|
||||
.format(3);
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I was trying to download this URL:"),
|
||||
alloc
|
||||
.string((&url).to_string())
|
||||
.annotate(Annotation::Url)
|
||||
.indent(4),
|
||||
alloc.concat([
|
||||
alloc.reflow(r"But the server stated this file is "),
|
||||
alloc.string(nice_bytes).annotate(Annotation::Keyword),
|
||||
alloc.reflow(r" in size. This is larger that the maximum size I can handle (around 32 GB)."),
|
||||
]),
|
||||
alloc.concat([
|
||||
alloc.tip(),
|
||||
alloc.reflow(r"Check that you have the correct URL for this package/platform. "),
|
||||
alloc.reflow(r"If you do, you should contact the package/platform's author and "),
|
||||
alloc.reflow(r"notify them about this issue."),
|
||||
]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename: "UNKNOWN.roc".into(),
|
||||
doc,
|
||||
title: "FILE TOO LARGE".to_string(),
|
||||
severity: Severity::Fatal,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_file_problem_report_string(filename: &Path, error: io::ErrorKind) -> String {
|
||||
let src_lines: Vec<&str> = Vec::new();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue