mod parse;
mod rewrite;
mod string_case;
mod trans;
use crate::{DeserializeEnv, RuleCore};
use ast_grep_core::meta_var::MetaVarEnv;
use ast_grep_core::meta_var::MetaVariable;
use ast_grep_core::Doc;
use ast_grep_core::Language;
use parse::ParseTransError;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use thiserror::Error;
pub use trans::Trans;
#[derive(Serialize, Deserialize, Clone, JsonSchema)]
#[serde(untagged)]
pub enum Transformation {
Simplied(String),
Object(Trans<String>),
}
impl Transformation {
pub fn parse<L: Language>(&self, lang: &L) -> Result<Trans<MetaVariable>, TransformError> {
match self {
Transformation::Simplied(s) => {
let t: Trans<String> = s.parse()?;
t.parse(lang)
}
Transformation::Object(t) => t.parse(lang),
}
}
}
#[derive(Debug, Error)]
pub enum TransformError {
#[error("Cannot parse transform string.")]
Parse(#[from] ParseTransError),
#[error("`{0}` has a cyclic dependency.")]
Cyclic(String),
#[error("Transform var `{0}` has already defined.")]
AlreadyDefined(String),
#[error("source `{0}` should be $-prefixed.")]
MalformedVar(String),
}
pub struct Transform {
transforms: Vec<(String, Trans<MetaVariable>)>,
}
impl Transform {
pub fn deserialize<L: Language>(
map: &HashMap<String, Transformation>,
env: &DeserializeEnv<L>,
) -> Result<Self, TransformError> {
let map: Result<_, _> = map
.iter()
.map(|(key, val)| val.parse(&env.lang).map(|t| (key.to_string(), t)))
.collect();
let map = map?;
let order = env
.get_transform_order(&map)
.map_err(TransformError::Cyclic)?;
let transforms = order
.iter()
.map(|&key| (key.to_string(), map[key].clone()))
.collect();
Ok(Self { transforms })
}
pub fn apply_transform<'c, D: Doc>(
&self,
env: &mut MetaVarEnv<'c, D>,
rewriters: &HashMap<String, RuleCore>,
enclosing_env: &MetaVarEnv<'c, D>,
) {
let mut ctx = Ctx {
env,
rewriters,
enclosing_env,
};
for (key, tr) in &self.transforms {
tr.insert(key, &mut ctx);
}
}
pub(crate) fn keys(&self) -> impl Iterator<Item = &String> {
self.transforms.iter().map(|t| &t.0)
}
pub(crate) fn values(&self) -> impl Iterator<Item = &Trans<MetaVariable>> {
self.transforms.iter().map(|t| &t.1)
}
}
struct Ctx<'b, 'c, D: Doc> {
rewriters: &'b HashMap<String, RuleCore>,
env: &'b mut MetaVarEnv<'c, D>,
enclosing_env: &'b MetaVarEnv<'c, D>,
}
#[cfg(test)]
mod test {
use super::*;
use crate::from_str;
use crate::test::TypeScript;
use ast_grep_core::tree_sitter::LanguageExt;
#[test]
fn test_transform_str() {}
#[test]
fn test_single_cyclic_transform() {
let mut trans = HashMap::new();
let trans_a = from_str("substring: {source: $A}").unwrap();
trans.insert("A".into(), trans_a);
let env = DeserializeEnv::new(TypeScript::Tsx);
match Transform::deserialize(&trans, &env) {
Err(TransformError::Cyclic(a)) => assert_eq!(a, "A"),
_ => panic!("unexpected error"),
}
}
#[test]
fn test_cyclic_transform() {
let mut trans = HashMap::new();
let trans_a = from_str("substring: {source: $B}").unwrap();
trans.insert("A".into(), trans_a);
let trans_b = from_str("substring: {source: $A}").unwrap();
trans.insert("B".into(), trans_b);
let env = DeserializeEnv::new(TypeScript::Tsx);
let ret = Transform::deserialize(&trans, &env);
assert!(matches!(ret, Err(TransformError::Cyclic(_))));
}
#[test]
fn test_transform_use_matched() {
let mut trans = HashMap::new();
let trans_a = from_str("substring: {source: $C}").unwrap();
trans.insert("A".into(), trans_a);
let trans_b = from_str("substring: {source: $A}").unwrap();
trans.insert("B".into(), trans_b);
let env = DeserializeEnv::new(TypeScript::Tsx);
let ret = Transform::deserialize(&trans, &env);
assert!(ret.is_ok());
}
#[test]
fn test_transform_indentation() {
let src = "
if (true) {
let a = {
b: 123
}
}
";
let expected = "{
b: 123
}";
let mut trans = HashMap::new();
let tr = from_str("{ substring: { source: $A } }").expect("should work");
trans.insert("TR".into(), tr);
let grep = TypeScript::Tsx.ast_grep(src);
let root = grep.root();
let mut nm = root.find("let a = $A").expect("should find");
let env = DeserializeEnv::new(TypeScript::Tsx);
let trans = Transform::deserialize(&trans, &env).expect("should deserialize");
trans.apply_transform(nm.get_env_mut(), &Default::default(), &Default::default());
let actual = nm.get_env().get_transformed("TR").expect("should have TR");
let actual = std::str::from_utf8(actual).expect("should work");
assert_eq!(actual, expected);
}
}