TEST_zap 0.6.1

A blazingly fast networking solution for Roblox.
Documentation
use codespan_reporting::diagnostic::{Diagnostic, Label, Severity};
use lalrpop_util::lexer::Token;

pub type Span = core::ops::Range<usize>;

#[derive(Debug, Clone)]
pub enum Report<'src> {
	LexerInvalidToken {
		span: Span,
	},

	ParserUnexpectedEOF {
		span: Span,
		expected: Vec<String>,
	},

	ParserUnexpectedToken {
		span: Span,
		expected: Vec<String>,
		token: Token<'src>,
	},

	ParserExtraToken {
		span: Span,
	},

	ParserExpectedInt {
		span: Span,
	},

	AnalyzeEmptyEvDecls,

	AnalyzeOversizeUnreliable {
		ev_span: Span,
		ty_span: Span,
		max_size: usize,
		size: usize,
	},

	AnalyzePotentiallyOversizeUnreliable {
		ev_span: Span,
		ty_span: Span,
		max_size: usize,
	},

	AnalyzeInvalidRange {
		span: Span,
	},

	AnalyzeEmptyEnum {
		span: Span,
	},

	AnalyzeEnumTagUsed {
		tag_span: Span,
		used_span: Span,
		tag: &'src str,
	},

	AnalyzeInvalidOptValue {
		span: Span,
		expected: &'static str,
	},

	AnalyzeUnknownOptName {
		span: Span,
	},

	AnalyzeUnknownTypeRef {
		span: Span,
		name: &'src str,
	},

	AnalyzeNumOutsideRange {
		span: Span,
		min: f64,
		max: f64,
	},

	AnalyzeInvalidOptionalType {
		span: Span,
	},

	AnalyzeUnboundedRecursiveType {
		decl_span: Span,
		use_span: Span,
	},

	AnalyzeMissingOptValue {
		expected: &'static str,
		required_when: &'static str,
	},

	AnalyzeDuplicateDecl {
		prev_span: Span,
		dup_span: Span,
		name: &'src str,
	},
}

impl<'src> Report<'src> {
	pub fn severity(&self) -> Severity {
		match self {
			Self::LexerInvalidToken { .. } => Severity::Error,

			Self::ParserUnexpectedEOF { .. } => Severity::Error,
			Self::ParserUnexpectedToken { .. } => Severity::Error,
			Self::ParserExtraToken { .. } => Severity::Error,
			Self::ParserExpectedInt { .. } => Severity::Error,

			Self::AnalyzeEmptyEvDecls => Severity::Warning,
			Self::AnalyzeOversizeUnreliable { .. } => Severity::Error,
			Self::AnalyzePotentiallyOversizeUnreliable { .. } => Severity::Warning,
			Self::AnalyzeInvalidRange { .. } => Severity::Error,
			Self::AnalyzeEmptyEnum { .. } => Severity::Error,
			Self::AnalyzeEnumTagUsed { .. } => Severity::Error,
			Self::AnalyzeInvalidOptValue { .. } => Severity::Error,
			Self::AnalyzeUnknownOptName { .. } => Severity::Warning,
			Self::AnalyzeUnknownTypeRef { .. } => Severity::Error,
			Self::AnalyzeNumOutsideRange { .. } => Severity::Error,
			Self::AnalyzeInvalidOptionalType { .. } => Severity::Error,
			Self::AnalyzeUnboundedRecursiveType { .. } => Severity::Error,
			Self::AnalyzeMissingOptValue { .. } => Severity::Error,
			Self::AnalyzeDuplicateDecl { .. } => Severity::Error,
		}
	}

	fn message(&self) -> String {
		match self {
			Self::LexerInvalidToken { .. } => "invalid token".to_string(),

			Self::ParserUnexpectedEOF { expected, .. } => {
				format!("expected {}, found end of file", expected.join(", "))
			}

			Self::ParserUnexpectedToken { expected, .. } => format!("expected {}", expected.join(", ")),
			Self::ParserExtraToken { .. } => "extra token".to_string(),
			Self::ParserExpectedInt { .. } => "expected integer".to_string(),

			Self::AnalyzeEmptyEvDecls => "no event or function declarations".to_string(),
			Self::AnalyzeOversizeUnreliable { .. } => "oversize unreliable".to_string(),
			Self::AnalyzePotentiallyOversizeUnreliable { .. } => "potentially oversize unreliable".to_string(),
			Self::AnalyzeInvalidRange { .. } => "invalid range".to_string(),
			Self::AnalyzeEmptyEnum { .. } => "empty enum".to_string(),
			Self::AnalyzeEnumTagUsed { .. } => "enum tag used in variant".to_string(),
			Self::AnalyzeInvalidOptValue { expected, .. } => format!("invalid opt value, expected {}", expected),
			Self::AnalyzeUnknownOptName { .. } => "unknown opt name".to_string(),
			Self::AnalyzeUnknownTypeRef { name, .. } => format!("unknown type reference '{}'", name),
			Self::AnalyzeNumOutsideRange { .. } => "number outside range".to_string(),
			Self::AnalyzeInvalidOptionalType { .. } => "invalid optional type".to_string(),
			Self::AnalyzeUnboundedRecursiveType { .. } => "unbounded recursive type".to_string(),
			Self::AnalyzeMissingOptValue { .. } => "missing option expected".to_string(),
			Self::AnalyzeDuplicateDecl { name, .. } => format!("duplicate declaration '{}'", name),
		}
	}

	fn code(&self) -> &str {
		match self {
			Self::LexerInvalidToken { .. } => "1001",

			Self::ParserUnexpectedEOF { .. } => "2001",
			Self::ParserUnexpectedToken { .. } => "2002",
			Self::ParserExtraToken { .. } => "2003",
			Self::ParserExpectedInt { .. } => "2004",

			Self::AnalyzeEmptyEvDecls { .. } => "3001",
			Self::AnalyzeOversizeUnreliable { .. } => "3002",
			Self::AnalyzePotentiallyOversizeUnreliable { .. } => "3003",
			Self::AnalyzeInvalidRange { .. } => "3004",
			Self::AnalyzeEmptyEnum { .. } => "3005",
			Self::AnalyzeEnumTagUsed { .. } => "3006",
			Self::AnalyzeInvalidOptValue { .. } => "3007",
			Self::AnalyzeUnknownOptName { .. } => "3008",
			Self::AnalyzeUnknownTypeRef { .. } => "3009",
			Self::AnalyzeNumOutsideRange { .. } => "3010",
			Self::AnalyzeInvalidOptionalType { .. } => "3011",
			Self::AnalyzeUnboundedRecursiveType { .. } => "3012",
			Self::AnalyzeMissingOptValue { .. } => "3013",
			Self::AnalyzeDuplicateDecl { .. } => "3014",
		}
	}

	fn labels(&self) -> Vec<Label<()>> {
		match self {
			Self::LexerInvalidToken { span } => vec![Label::primary((), span.clone()).with_message("invalid token")],

			Self::ParserUnexpectedEOF { span, .. } => {
				vec![Label::primary((), span.clone()).with_message("unexpected end of file")]
			}

			Self::ParserUnexpectedToken { span, .. } => {
				vec![Label::primary((), span.clone()).with_message("unexpected token")]
			}

			Self::ParserExtraToken { span } => {
				vec![Label::primary((), span.clone()).with_message("extra token")]
			}

			Self::ParserExpectedInt { span } => {
				vec![Label::primary((), span.clone()).with_message("expected integer")]
			}

			Self::AnalyzeEmptyEvDecls => vec![],

			Self::AnalyzeOversizeUnreliable { ev_span, ty_span, .. } => {
				vec![
					Label::primary((), ev_span.clone()).with_message("event is unreliable"),
					Label::secondary((), ty_span.clone()).with_message("type is too large"),
				]
			}

			Self::AnalyzePotentiallyOversizeUnreliable { ev_span, ty_span, .. } => {
				vec![
					Label::primary((), ev_span.clone()).with_message("event is unreliable"),
					Label::secondary((), ty_span.clone()).with_message("type may be too large"),
				]
			}

			Self::AnalyzeInvalidRange { span } => {
				vec![Label::primary((), span.clone()).with_message("invalid range")]
			}

			Self::AnalyzeEmptyEnum { span } => {
				vec![Label::primary((), span.clone()).with_message("empty enum")]
			}

			Self::AnalyzeEnumTagUsed {
				tag_span, used_span, ..
			} => {
				vec![
					Label::primary((), tag_span.clone()).with_message("enum tag"),
					Label::secondary((), used_span.clone()).with_message("used in variant"),
				]
			}

			Self::AnalyzeInvalidOptValue { span, .. } => {
				vec![Label::primary((), span.clone()).with_message("invalid opt value")]
			}

			Self::AnalyzeUnknownOptName { span } => {
				vec![Label::primary((), span.clone()).with_message("unknown opt name")]
			}

			Self::AnalyzeUnknownTypeRef { span, .. } => {
				vec![Label::primary((), span.clone()).with_message("unknown type reference")]
			}

			Self::AnalyzeNumOutsideRange { span, .. } => {
				vec![Label::primary((), span.clone()).with_message("number outside range")]
			}

			Self::AnalyzeInvalidOptionalType { span, .. } => {
				vec![Label::primary((), span.clone()).with_message("must be removed")]
			}

			Self::AnalyzeUnboundedRecursiveType {
				decl_span, use_span, ..
			} => {
				vec![
					Label::secondary((), decl_span.clone()).with_message("declared here"),
					Label::primary((), use_span.clone()).with_message("used recursively here"),
				]
			}

			Self::AnalyzeMissingOptValue { .. } => vec![],

			Self::AnalyzeDuplicateDecl {
				prev_span, dup_span, ..
			} => {
				vec![
					Label::secondary((), prev_span.clone()).with_message("previous declaration"),
					Label::primary((), dup_span.clone()).with_message("duplicate declaration"),
				]
			}
		}
	}

	fn notes(&self) -> Option<Vec<String>> {
		match self {
			Self::LexerInvalidToken { .. } => None,

			Self::ParserUnexpectedEOF { .. } => None,
			Self::ParserUnexpectedToken { .. } => None,
			Self::ParserExtraToken { .. } => None,
			Self::ParserExpectedInt { .. } => None,

			Self::AnalyzeEmptyEvDecls => Some(vec![
				"add an event or function declaration to allow zap to output code".to_string()
			]),
			Self::AnalyzeOversizeUnreliable { max_size, .. } => Some(vec![
				format!("all unreliable events must be under {max_size} bytes in size"),
				"consider adding a upper limit to any arrays or strings".to_string(),
				"upper limits can be added for arrays by doing `[..10]`".to_string(),
				"upper limits can be added for strings by doing `(..10)`".to_string(),
			]),
			Self::AnalyzePotentiallyOversizeUnreliable { max_size, .. } => Some(vec![
				format!("all unreliable events must be under {max_size} bytes in size"),
				"consider adding a upper limit to any arrays or strings".to_string(),
				"upper limits can be added for arrays by doing `[..10]`".to_string(),
				"upper limits can be added for strings by doing `(..10)`".to_string(),
			]),
			Self::AnalyzeInvalidRange { .. } => Some(vec![
				"ranges must be in the form `min..max`".to_string(),
				"ranges can be invalid if `min` is greater than `max`".to_string(),
			]),
			Self::AnalyzeEmptyEnum { .. } => Some(vec![
				"enums cannot be empty".to_string(),
				"if you're looking to create an empty type, use a struct with no fields".to_string(),
				"a struct with no fields can be created by doing `struct {}`".to_string(),
			]),
			Self::AnalyzeEnumTagUsed { .. } => Some(vec![
				"tagged enums use the tag field in passed structs to determine what variant to use".to_string(),
				"you cannot override this tag field in a variant as that would break this behavior".to_string(),
			]),
			Self::AnalyzeInvalidOptValue { .. } => None,
			Self::AnalyzeUnknownOptName { .. } => None,
			Self::AnalyzeUnknownTypeRef { .. } => None,
			Self::AnalyzeNumOutsideRange { min, max, .. } => Some(vec![
				format!("(inclusive) min: {}", min),
				format!("(inclusive) max: {}", max),
			]),
			Self::AnalyzeInvalidOptionalType { .. } => Some(vec![
				"you cannot have 'double optional' types, where a type is optional twice".to_string(),
				"maps cannot have optional keys or values, as those are impossible to represent in luau".to_string(),
				"additionally the `unknown` type cannot be optional".to_string(),
			]),
			Self::AnalyzeUnboundedRecursiveType { .. } => Some(vec![
				"this is an unbounded recursive type".to_string(),
				"unbounded recursive types cause infinite loops".to_string(),
			]),
			Self::AnalyzeMissingOptValue {
				expected,
				required_when,
			} => Some(vec![format!(
				"the {expected} option should not be empty if {required_when}"
			)]),
			Self::AnalyzeDuplicateDecl { .. } => None,
		}
	}
}

impl<'src> From<Report<'src>> for Diagnostic<()> {
	fn from(val: Report<'src>) -> Self {
		let diagnostic = Diagnostic::new(val.severity())
			.with_code(val.code())
			.with_message(val.message())
			.with_labels(val.labels());

		if let Some(notes) = val.notes() {
			diagnostic.with_notes(notes)
		} else {
			diagnostic
		}
	}
}