use crate::BackendDependency;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum SourceLanguage {
TypeScript,
Python,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ApiNamespace {
Json,
Regex,
Random,
Http,
DateTime,
Url,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ReceiverKind {
FreeFunction,
Namespace,
Instance,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ApiShape {
Call,
Constructor,
Property,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ArgShape {
OneValue,
OneString,
TwoStrings,
TwoInts,
OneNumber,
None,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ReturnShape {
Bool,
String,
Float,
Int,
Contextual,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum EffectKind {
Pure,
Random,
Io,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
#[non_exhaustive]
pub enum RuleId {
TsJsonStringify,
TsJsonParse,
TsRegExpTest,
TsMathRandom,
TsFetch,
TsDateNow,
TsDateToIsoString,
TsUrlField,
TsStructuredClone,
TsPromiseStatic,
TsPrimitiveCast,
TsSymbol,
TsMathNumeric,
TsNumberPredicate,
TsNumberParseFloat,
TsNumberParseInt,
TsObjectStatic,
TsArrayStatic,
TsBufferStatic,
TsMapHas,
TsMapGet,
TsMapMutation,
TsMapProjection,
TsSetHas,
TsSetMutation,
TsSetProjection,
PyJsonDumps,
PyJsonLoads,
PyReSearch,
PyReMatch,
PyReFullMatch,
PyRandomRandom,
PyRandomRandInt,
PyRandomChoice,
PyRequestsGet,
PyDateTimeNow,
PyDateTimeFromTimestamp,
PyUrlparseField,
}
impl RuleId {
#[must_use]
pub const fn backend_dependency(self) -> Option<BackendDependency> {
match self {
Self::TsJsonStringify | Self::TsJsonParse | Self::PyJsonDumps | Self::PyJsonLoads => {
Some(BackendDependency::SerdeJson)
}
Self::TsRegExpTest | Self::PyReSearch | Self::PyReMatch | Self::PyReFullMatch => {
Some(BackendDependency::Regex)
}
Self::TsMathRandom
| Self::PyRandomRandom
| Self::PyRandomRandInt
| Self::PyRandomChoice => Some(BackendDependency::Rand),
Self::TsFetch | Self::PyRequestsGet => Some(BackendDependency::Reqwest),
Self::TsDateNow
| Self::TsDateToIsoString
| Self::PyDateTimeNow
| Self::PyDateTimeFromTimestamp => Some(BackendDependency::Chrono),
Self::TsUrlField | Self::PyUrlparseField => Some(BackendDependency::Url),
Self::TsStructuredClone
| Self::TsPromiseStatic
| Self::TsPrimitiveCast
| Self::TsSymbol
| Self::TsMathNumeric
| Self::TsNumberPredicate
| Self::TsNumberParseFloat
| Self::TsNumberParseInt
| Self::TsObjectStatic
| Self::TsArrayStatic
| Self::TsBufferStatic
| Self::TsMapHas
| Self::TsMapGet
| Self::TsMapMutation
| Self::TsMapProjection
| Self::TsSetHas
| Self::TsSetMutation
| Self::TsSetProjection => None,
}
}
#[must_use]
pub const fn source_api(self) -> &'static str {
match self {
Self::TsJsonStringify => "JSON.stringify",
Self::TsJsonParse => "JSON.parse",
Self::TsRegExpTest => "RegExp.test",
Self::TsMathRandom => "Math.random",
Self::TsFetch => "fetch",
Self::TsDateNow => "Date.now",
Self::TsDateToIsoString => "Date.toISOString",
Self::TsUrlField => "URL field access",
Self::TsStructuredClone => "structuredClone",
Self::TsPromiseStatic => "Promise static method",
Self::TsPrimitiveCast => "primitive conversion",
Self::TsSymbol => "Symbol",
Self::TsMathNumeric => "Math numeric method",
Self::TsNumberPredicate => "Number predicate",
Self::TsNumberParseFloat => "Number.parseFloat",
Self::TsNumberParseInt => "Number.parseInt",
Self::TsObjectStatic => "Object static method",
Self::TsArrayStatic => "Array static method",
Self::TsBufferStatic => "Buffer static method",
Self::TsMapHas => "Map.has",
Self::TsMapGet => "Map.get",
Self::TsMapMutation => "Map mutation method",
Self::TsMapProjection => "Map projection method",
Self::TsSetHas => "Set.has",
Self::TsSetMutation => "Set mutation method",
Self::TsSetProjection => "Set projection method",
Self::PyJsonDumps => "json.dumps",
Self::PyJsonLoads => "json.loads",
Self::PyReSearch => "re.search",
Self::PyReMatch => "re.match",
Self::PyReFullMatch => "re.fullmatch",
Self::PyRandomRandom => "random.random",
Self::PyRandomRandInt => "random.randint",
Self::PyRandomChoice => "random.choice",
Self::PyRequestsGet => "requests.get",
Self::PyDateTimeNow => "datetime.datetime.now",
Self::PyDateTimeFromTimestamp => "datetime.datetime.fromtimestamp",
Self::PyUrlparseField => "urllib.parse.urlparse field access",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dependency_backed_date_and_url_rules_report_metadata() {
let cases = [
(RuleId::TsDateNow, BackendDependency::Chrono, "Date.now"),
(
RuleId::TsDateToIsoString,
BackendDependency::Chrono,
"Date.toISOString",
),
(
RuleId::PyDateTimeNow,
BackendDependency::Chrono,
"datetime.datetime.now",
),
(
RuleId::PyDateTimeFromTimestamp,
BackendDependency::Chrono,
"datetime.datetime.fromtimestamp",
),
(
RuleId::TsUrlField,
BackendDependency::Url,
"URL field access",
),
(
RuleId::PyUrlparseField,
BackendDependency::Url,
"urllib.parse.urlparse field access",
),
];
for (rule, dependency, source_api) in cases {
assert_eq!(rule.backend_dependency(), Some(dependency));
assert_eq!(rule.source_api(), source_api);
}
}
}