use std::sync::Arc;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureFile {
pub attrs: Vec<PureAttribute>,
pub items: Vec<PureItem>,
}
unsafe impl Send for PureFile {}
unsafe impl Sync for PureFile {}
impl PureFile {
pub fn new() -> Self {
Self {
attrs: Vec::new(),
items: Vec::new(),
}
}
pub fn functions(&self) -> Vec<&PureFn> {
self.items
.iter()
.filter_map(|item| {
if let PureItem::Fn(f) = item {
Some(f)
} else {
None
}
})
.collect()
}
pub fn structs(&self) -> Vec<&PureStruct> {
self.items
.iter()
.filter_map(|item| {
if let PureItem::Struct(s) = item {
Some(s)
} else {
None
}
})
.collect()
}
pub fn uses(&self) -> Vec<&PureUse> {
self.items
.iter()
.filter_map(|item| {
if let PureItem::Use(u) = item {
Some(u)
} else {
None
}
})
.collect()
}
pub fn find_fn(&self, name: &str) -> Option<&PureFn> {
self.functions().into_iter().find(|f| f.name == name)
}
pub fn traits(&self) -> Vec<&PureTrait> {
self.items
.iter()
.filter_map(|item| {
if let PureItem::Trait(t) = item {
Some(t)
} else {
None
}
})
.collect()
}
pub fn impls(&self) -> Vec<&PureImpl> {
self.items
.iter()
.filter_map(|item| {
if let PureItem::Impl(i) = item {
Some(i)
} else {
None
}
})
.collect()
}
pub fn into_arc(self) -> Arc<Self> {
Arc::new(self)
}
}
impl Default for PureFile {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PureItem {
Use(PureUse),
Fn(PureFn),
Struct(PureStruct),
Enum(PureEnum),
Impl(PureImpl),
Const(PureConst),
Static(PureStatic),
Type(PureTypeAlias),
Mod(PureMod),
Trait(PureTrait),
Macro(PureMacro),
Other(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PureAttrMeta {
Path,
List(String),
NameValue(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureAttribute {
pub path: String,
pub meta: PureAttrMeta,
pub is_inner: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureUse {
pub vis: PureVis,
pub tree: PureUseTree,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PureUseTree {
Path {
path: String,
tree: Box<PureUseTree>,
},
Name(String),
Rename {
name: String,
rename: String,
},
Glob,
Group(Vec<PureUseTree>),
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PureVis {
#[default]
Private,
Public,
Crate,
Super,
In(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureFn {
pub attrs: Vec<PureAttribute>,
pub vis: PureVis,
pub is_async: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub is_async_inferred: bool,
pub is_const: bool,
pub is_unsafe: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub abi: Option<String>,
pub name: String,
pub generics: PureGenerics,
pub params: Vec<PureParam>,
pub ret: Option<PureType>,
pub body: PureBlock,
}
impl PureFn {
pub fn is_effectively_async(&self) -> bool {
self.is_async || self.is_async_inferred
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PureParam {
SelfValue {
is_ref: bool,
is_mut: bool,
},
Typed {
name: String,
ty: PureType,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureClosureParam {
pub pattern: PurePattern,
pub ty: Option<PureType>,
}
impl PureClosureParam {
pub fn untyped(pattern: PurePattern) -> Self {
Self { pattern, ty: None }
}
pub fn typed(pattern: PurePattern, ty: PureType) -> Self {
Self {
pattern,
ty: Some(ty),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureBlock {
pub stmts: Vec<PureStmt>,
}
impl PureBlock {
#[inline]
pub fn get_stmt(&self, index: usize) -> Option<&PureStmt> {
self.stmts.get(index)
}
#[inline]
pub fn get_stmt_mut(&mut self, index: usize) -> Option<&mut PureStmt> {
self.stmts.get_mut(index)
}
#[inline]
pub fn len(&self) -> usize {
self.stmts.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.stmts.is_empty()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PureStmt {
Local {
pattern: PurePattern,
ty: Option<PureType>,
init: Option<PureExpr>,
},
Semi(PureExpr),
Expr(PureExpr),
Item(Box<PureItem>),
}
impl PureStmt {
pub fn get_expr(&self) -> Option<&PureExpr> {
match self {
PureStmt::Local { init, .. } => init.as_ref(),
PureStmt::Semi(e) | PureStmt::Expr(e) => Some(e),
PureStmt::Item(_) => None,
}
}
pub fn get_expr_mut(&mut self) -> Option<&mut PureExpr> {
match self {
PureStmt::Local { init, .. } => init.as_mut(),
PureStmt::Semi(e) | PureStmt::Expr(e) => Some(e),
PureStmt::Item(_) => None,
}
}
pub fn has_expr(&self) -> bool {
match self {
PureStmt::Local { init, .. } => init.is_some(),
PureStmt::Semi(_) | PureStmt::Expr(_) => true,
PureStmt::Item(_) => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PurePattern {
Ident {
name: String,
is_mut: bool,
},
Wild,
Tuple(Vec<PurePattern>),
Struct {
path: String,
fields: Vec<(String, PurePattern)>,
rest: bool,
},
Ref {
is_mut: bool,
pattern: Box<PurePattern>,
},
Lit(String),
Or(Vec<PurePattern>),
Path(String),
Range {
start: Option<String>,
end: Option<String>,
inclusive: bool,
},
Slice(Vec<PurePattern>),
Rest,
Other(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PureExpr {
Lit(String),
Path(String),
Binary {
op: String,
left: Box<PureExpr>,
right: Box<PureExpr>,
},
Unary {
op: String,
expr: Box<PureExpr>,
},
Call {
func: Box<PureExpr>,
args: Vec<PureExpr>,
},
MethodCall {
receiver: Box<PureExpr>,
method: String,
turbofish: Option<String>,
args: Vec<PureExpr>,
},
Field {
expr: Box<PureExpr>,
field: String,
},
Index {
expr: Box<PureExpr>,
index: Box<PureExpr>,
},
Block {
label: Option<String>,
block: PureBlock,
},
If {
cond: Box<PureExpr>,
then_branch: PureBlock,
else_branch: Option<Box<PureExpr>>,
},
Match {
expr: Box<PureExpr>,
arms: Vec<PureMatchArm>,
},
Loop {
label: Option<String>,
body: PureBlock,
},
While {
label: Option<String>,
cond: Box<PureExpr>,
body: PureBlock,
},
For {
label: Option<String>,
pat: PurePattern,
expr: Box<PureExpr>,
body: PureBlock,
},
Return(Option<Box<PureExpr>>),
Break {
label: Option<String>,
expr: Option<Box<PureExpr>>,
},
Continue {
label: Option<String>,
},
Closure {
is_async: bool,
is_move: bool,
params: Vec<PureClosureParam>,
ret: Option<PureType>,
body: Box<PureExpr>,
},
Struct {
path: String,
fields: Vec<(String, PureExpr)>,
},
Tuple(Vec<PureExpr>),
Array(Vec<PureExpr>),
Ref {
is_mut: bool,
expr: Box<PureExpr>,
},
Macro {
name: String,
delimiter: MacroDelimiter,
tokens: String,
},
Await(Box<PureExpr>),
Try(Box<PureExpr>),
Range {
start: Option<Box<PureExpr>>,
end: Option<Box<PureExpr>>,
inclusive: bool,
},
Cast {
expr: Box<PureExpr>,
ty: PureType,
},
Let {
pattern: PurePattern,
expr: Box<PureExpr>,
},
Async {
is_move: bool,
body: PureBlock,
},
Unsafe(PureBlock),
Repeat {
expr: Box<PureExpr>,
len: Box<PureExpr>,
},
Other(String),
}
impl PureExpr {
pub fn method_call(receiver: Box<PureExpr>, method: String, args: Vec<PureExpr>) -> Self {
PureExpr::MethodCall {
receiver,
method,
turbofish: None,
args,
}
}
pub fn closure(params: Vec<PureClosureParam>, body: Box<PureExpr>) -> Self {
PureExpr::Closure {
is_async: false,
is_move: false,
params,
ret: None,
body,
}
}
pub fn get_child(&self, index: usize) -> Option<&PureExpr> {
match self {
PureExpr::Unary { expr, .. }
| PureExpr::Field { expr, .. }
| PureExpr::Ref { expr, .. }
| PureExpr::Await(expr)
| PureExpr::Try(expr)
| PureExpr::Cast { expr, .. }
| PureExpr::Let { expr, .. } => {
if index == 0 {
Some(expr)
} else {
None
}
}
PureExpr::Binary { left, right, .. } => match index {
0 => Some(left),
1 => Some(right),
_ => None,
},
PureExpr::Index { expr, index: idx } => match index {
0 => Some(expr),
1 => Some(idx),
_ => None,
},
PureExpr::Repeat { expr, len } => match index {
0 => Some(expr),
1 => Some(len),
_ => None,
},
PureExpr::Call { func, args } => {
if index == 0 {
Some(func)
} else {
args.get(index - 1)
}
}
PureExpr::MethodCall { receiver, args, .. } => {
if index == 0 {
Some(receiver)
} else {
args.get(index - 1)
}
}
PureExpr::Range { start, end, .. } => match index {
0 => start.as_deref(),
1 => end.as_deref(),
_ => None,
},
PureExpr::If {
cond, else_branch, ..
} => match index {
0 => Some(cond),
1 => else_branch.as_deref(),
_ => None,
},
PureExpr::Return(opt) => {
if index == 0 {
opt.as_deref()
} else {
None
}
}
PureExpr::Break { expr: opt, .. } => {
if index == 0 {
opt.as_deref()
} else {
None
}
}
PureExpr::While { cond, .. } => {
if index == 0 {
Some(cond)
} else {
None
}
}
PureExpr::For { expr, .. } => {
if index == 0 {
Some(expr)
} else {
None
}
}
PureExpr::Match { expr, .. } => {
if index == 0 {
Some(expr)
} else {
None
}
}
PureExpr::Closure { body, .. } => {
if index == 0 {
Some(body)
} else {
None
}
}
PureExpr::Tuple(exprs) | PureExpr::Array(exprs) => exprs.get(index),
PureExpr::Struct { fields, .. } => fields.get(index).map(|(_, e)| e),
PureExpr::Lit(_)
| PureExpr::Path(_)
| PureExpr::Block { .. }
| PureExpr::Loop { .. }
| PureExpr::Async { .. }
| PureExpr::Unsafe(_)
| PureExpr::Continue { .. }
| PureExpr::Macro { .. }
| PureExpr::Other(_) => None,
}
}
pub fn get_child_mut(&mut self, index: usize) -> Option<&mut PureExpr> {
match self {
PureExpr::Unary { expr, .. }
| PureExpr::Field { expr, .. }
| PureExpr::Ref { expr, .. }
| PureExpr::Await(expr)
| PureExpr::Try(expr)
| PureExpr::Cast { expr, .. }
| PureExpr::Let { expr, .. } => {
if index == 0 {
Some(expr)
} else {
None
}
}
PureExpr::Binary { left, right, .. } => match index {
0 => Some(left),
1 => Some(right),
_ => None,
},
PureExpr::Index { expr, index: idx } => match index {
0 => Some(expr),
1 => Some(idx),
_ => None,
},
PureExpr::Repeat { expr, len } => match index {
0 => Some(expr),
1 => Some(len),
_ => None,
},
PureExpr::Call { func, args } => {
if index == 0 {
Some(func)
} else {
args.get_mut(index - 1)
}
}
PureExpr::MethodCall { receiver, args, .. } => {
if index == 0 {
Some(receiver)
} else {
args.get_mut(index - 1)
}
}
PureExpr::Range { start, end, .. } => match index {
0 => start.as_deref_mut(),
1 => end.as_deref_mut(),
_ => None,
},
PureExpr::If {
cond, else_branch, ..
} => match index {
0 => Some(cond.as_mut()),
1 => else_branch.as_deref_mut(),
_ => None,
},
PureExpr::Return(opt) => {
if index == 0 {
opt.as_deref_mut()
} else {
None
}
}
PureExpr::Break { expr: opt, .. } => {
if index == 0 {
opt.as_deref_mut()
} else {
None
}
}
PureExpr::While { cond, .. } => {
if index == 0 {
Some(cond)
} else {
None
}
}
PureExpr::For { expr, .. } => {
if index == 0 {
Some(expr)
} else {
None
}
}
PureExpr::Match { expr, .. } => {
if index == 0 {
Some(expr)
} else {
None
}
}
PureExpr::Closure { body, .. } => {
if index == 0 {
Some(body)
} else {
None
}
}
PureExpr::Tuple(exprs) | PureExpr::Array(exprs) => exprs.get_mut(index),
PureExpr::Struct { fields, .. } => fields.get_mut(index).map(|(_, e)| e),
PureExpr::Lit(_)
| PureExpr::Path(_)
| PureExpr::Block { .. }
| PureExpr::Loop { .. }
| PureExpr::Async { .. }
| PureExpr::Unsafe(_)
| PureExpr::Continue { .. }
| PureExpr::Macro { .. }
| PureExpr::Other(_) => None,
}
}
pub fn get_block(&self) -> Option<&PureBlock> {
match self {
PureExpr::Block { block: b, .. }
| PureExpr::Loop { body: b, .. }
| PureExpr::Unsafe(b) => Some(b),
PureExpr::Async { body, .. } => Some(body),
PureExpr::If { then_branch, .. } => Some(then_branch),
PureExpr::While { body, .. } | PureExpr::For { body, .. } => Some(body),
_ => None,
}
}
pub fn get_block_mut(&mut self) -> Option<&mut PureBlock> {
match self {
PureExpr::Block { block: b, .. }
| PureExpr::Loop { body: b, .. }
| PureExpr::Unsafe(b) => Some(b),
PureExpr::Async { body, .. } => Some(body),
PureExpr::If { then_branch, .. } => Some(then_branch),
PureExpr::While { body, .. } | PureExpr::For { body, .. } => Some(body),
_ => None,
}
}
pub fn child_count(&self) -> usize {
match self {
PureExpr::Lit(_)
| PureExpr::Path(_)
| PureExpr::Continue { .. }
| PureExpr::Macro { .. }
| PureExpr::Other(_)
| PureExpr::Block { .. }
| PureExpr::Loop { .. }
| PureExpr::Async { .. }
| PureExpr::Unsafe(_) => 0,
PureExpr::Unary { .. }
| PureExpr::Field { .. }
| PureExpr::Ref { .. }
| PureExpr::Await(_)
| PureExpr::Try(_)
| PureExpr::Cast { .. }
| PureExpr::Let { .. }
| PureExpr::Closure { .. }
| PureExpr::While { .. }
| PureExpr::For { .. }
| PureExpr::Match { .. } => 1,
PureExpr::Binary { .. } | PureExpr::Index { .. } | PureExpr::Repeat { .. } => 2,
PureExpr::Range { start, end, .. } => start.is_some() as usize + end.is_some() as usize,
PureExpr::If { else_branch, .. } => 1 + else_branch.is_some() as usize,
PureExpr::Return(opt) => opt.is_some() as usize,
PureExpr::Break { expr: opt, .. } => opt.is_some() as usize,
PureExpr::Call { args, .. } => 1 + args.len(),
PureExpr::MethodCall { args, .. } => 1 + args.len(),
PureExpr::Tuple(v) | PureExpr::Array(v) => v.len(),
PureExpr::Struct { fields, .. } => fields.len(),
}
}
pub fn navigate(&self, path: &[usize]) -> Option<&PureExpr> {
let mut current = self;
for &idx in path {
current = current.get_child(idx)?;
}
Some(current)
}
pub fn navigate_mut(&mut self, path: &[usize]) -> Option<&mut PureExpr> {
let mut current = self;
for &idx in path {
current = current.get_child_mut(idx)?;
}
Some(current)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureMatchArm {
pub pattern: PurePattern,
pub guard: Option<PureExpr>,
pub body: PureExpr,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PureType {
Path(String),
Ref {
lifetime: Option<String>,
is_mut: bool,
ty: Box<PureType>,
},
Tuple(Vec<PureType>),
Array {
ty: Box<PureType>,
len: String,
},
Slice(Box<PureType>),
Fn {
params: Vec<PureType>,
ret: Option<Box<PureType>>,
},
ImplTrait(Vec<String>),
TraitObject(Vec<String>),
Infer,
Never,
Other(String),
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureGenerics {
pub params: Vec<PureGenericParam>,
pub where_clause: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PureGenericParam {
Type {
name: String,
bounds: Vec<String>,
},
Lifetime {
name: String,
bounds: Vec<String>,
},
Const {
name: String,
ty: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureStruct {
pub attrs: Vec<PureAttribute>,
pub vis: PureVis,
pub name: String,
pub generics: PureGenerics,
pub fields: PureFields,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PureFields {
Named(Vec<PureField>),
Tuple(Vec<PureType>),
Unit,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureField {
pub attrs: Vec<PureAttribute>,
pub vis: PureVis,
pub name: String,
pub ty: PureType,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureEnum {
pub attrs: Vec<PureAttribute>,
pub vis: PureVis,
pub name: String,
pub generics: PureGenerics,
pub variants: Vec<PureVariant>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureVariant {
pub attrs: Vec<PureAttribute>,
pub name: String,
pub fields: PureFields,
pub discriminant: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureImpl {
pub attrs: Vec<PureAttribute>,
pub generics: PureGenerics,
pub is_unsafe: bool,
pub trait_: Option<String>,
pub self_ty: String,
pub items: Vec<PureImplItem>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PureImplItem {
Fn(PureFn),
Const(PureConst),
Type(PureTypeAlias),
Other(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureConst {
pub attrs: Vec<PureAttribute>,
pub vis: PureVis,
pub name: String,
pub ty: PureType,
pub value: Option<PureExpr>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureStatic {
pub attrs: Vec<PureAttribute>,
pub vis: PureVis,
pub is_mut: bool,
pub name: String,
pub ty: PureType,
pub value: PureExpr,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureTypeAlias {
pub attrs: Vec<PureAttribute>,
pub vis: PureVis,
pub name: String,
pub generics: PureGenerics,
pub ty: PureType,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureMod {
pub attrs: Vec<PureAttribute>,
pub vis: PureVis,
pub name: String,
pub items: Vec<PureItem>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureTrait {
pub attrs: Vec<PureAttribute>,
pub vis: PureVis,
pub is_unsafe: bool,
pub is_auto: bool,
pub name: String,
pub generics: PureGenerics,
pub supertraits: Vec<String>,
pub items: Vec<PureTraitItem>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PureTraitItem {
Fn(PureFn),
Const(PureConst),
Type {
name: String,
bounds: Vec<String>,
default: Option<PureType>,
},
Other(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PureMacro {
pub path: String,
pub delimiter: MacroDelimiter,
pub tokens: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum MacroDelimiter {
Paren,
Brace,
Bracket,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pure_file_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<PureFile>();
}
#[test]
#[cfg(feature = "serde")]
fn test_serde_json_roundtrip() {
let file = PureFile::from_source(
r#"
use std::io;
fn hello(name: &str) -> String {
format!("Hello, {}!", name)
}
struct Point {
x: i32,
y: i32,
}
"#,
)
.unwrap();
let json = serde_json::to_string_pretty(&file).unwrap();
assert!(json.contains("hello"));
assert!(json.contains("Point"));
let restored: PureFile = serde_json::from_str(&json).unwrap();
assert_eq!(file, restored);
}
#[test]
#[cfg(feature = "serde")]
fn test_serde_compact() {
let file = PureFile::from_source("fn main() {}").unwrap();
let json = serde_json::to_string(&file).unwrap();
assert!(json.len() < 500);
let restored: PureFile = serde_json::from_str(&json).unwrap();
assert_eq!(file, restored);
}
#[test]
#[cfg(feature = "serde")]
fn test_serde_complex_ast() {
let file = PureFile::from_source(
r#"
pub struct Config<T: Clone> {
pub name: String,
pub value: T,
}
impl<T: Clone> Config<T> {
pub fn new(name: String, value: T) -> Self {
Self { name, value }
}
}
pub trait Configurable {
fn config(&self) -> &str;
}
"#,
)
.unwrap();
let json = serde_json::to_string(&file).unwrap();
let restored: PureFile = serde_json::from_str(&json).unwrap();
assert_eq!(file, restored);
assert_eq!(restored.structs().len(), 1);
assert_eq!(restored.structs()[0].name, "Config");
}
#[test]
fn test_pure_block_navigation() {
let block = PureBlock {
stmts: vec![
PureStmt::Semi(PureExpr::Lit("1".into())),
PureStmt::Semi(PureExpr::Lit("2".into())),
PureStmt::Expr(PureExpr::Lit("3".into())),
],
};
assert_eq!(block.len(), 3);
assert!(!block.is_empty());
let stmt0 = block.get_stmt(0).unwrap();
assert!(matches!(stmt0, PureStmt::Semi(PureExpr::Lit(s)) if s == "1"));
let stmt2 = block.get_stmt(2).unwrap();
assert!(matches!(stmt2, PureStmt::Expr(PureExpr::Lit(s)) if s == "3"));
assert!(block.get_stmt(3).is_none());
}
#[test]
fn test_pure_stmt_get_expr() {
let semi = PureStmt::Semi(PureExpr::Lit("x".into()));
assert!(semi.has_expr());
assert!(matches!(semi.get_expr(), Some(PureExpr::Lit(_))));
let local_with_init = PureStmt::Local {
pattern: PurePattern::Ident {
name: "x".into(),
is_mut: false,
},
ty: None,
init: Some(PureExpr::Lit("42".into())),
};
assert!(local_with_init.has_expr());
assert!(matches!(local_with_init.get_expr(), Some(PureExpr::Lit(_))));
let local_no_init = PureStmt::Local {
pattern: PurePattern::Ident {
name: "x".into(),
is_mut: false,
},
ty: None,
init: None,
};
assert!(!local_no_init.has_expr());
assert!(local_no_init.get_expr().is_none());
}
#[test]
fn test_pure_expr_get_child_binary() {
let expr = PureExpr::Binary {
op: "+".into(),
left: Box::new(PureExpr::Path("a".into())),
right: Box::new(PureExpr::Path("b".into())),
};
assert_eq!(expr.child_count(), 2);
let left = expr.get_child(0).unwrap();
assert!(matches!(left, PureExpr::Path(s) if s == "a"));
let right = expr.get_child(1).unwrap();
assert!(matches!(right, PureExpr::Path(s) if s == "b"));
assert!(expr.get_child(2).is_none());
}
#[test]
fn test_pure_expr_get_child_method_call() {
let expr = PureExpr::MethodCall {
receiver: Box::new(PureExpr::Path("x".into())),
method: "foo".into(),
turbofish: None,
args: vec![PureExpr::Path("a".into()), PureExpr::Path("b".into())],
};
assert_eq!(expr.child_count(), 3);
let receiver = expr.get_child(0).unwrap();
assert!(matches!(receiver, PureExpr::Path(s) if s == "x"));
let arg0 = expr.get_child(1).unwrap();
assert!(matches!(arg0, PureExpr::Path(s) if s == "a"));
let arg1 = expr.get_child(2).unwrap();
assert!(matches!(arg1, PureExpr::Path(s) if s == "b"));
assert!(expr.get_child(3).is_none());
}
#[test]
fn test_pure_expr_navigate() {
let expr = PureExpr::MethodCall {
receiver: Box::new(PureExpr::Path("x".into())),
method: "foo".into(),
turbofish: None,
args: vec![
PureExpr::Binary {
op: "+".into(),
left: Box::new(PureExpr::Path("a".into())),
right: Box::new(PureExpr::Path("b".into())),
},
PureExpr::Path("c".into()),
],
};
let r = expr.navigate(&[0]).unwrap();
assert!(matches!(r, PureExpr::Path(s) if s == "x"));
let arg0 = expr.navigate(&[1]).unwrap();
assert!(matches!(arg0, PureExpr::Binary { .. }));
let a = expr.navigate(&[1, 0]).unwrap();
assert!(matches!(a, PureExpr::Path(s) if s == "a"));
let b = expr.navigate(&[1, 1]).unwrap();
assert!(matches!(b, PureExpr::Path(s) if s == "b"));
assert!(expr.navigate(&[1, 2]).is_none());
assert!(expr.navigate(&[5]).is_none());
}
#[test]
fn test_pure_expr_get_block() {
let block_expr = PureExpr::Block {
label: None,
block: PureBlock {
stmts: vec![PureStmt::Expr(PureExpr::Lit("1".into()))],
},
};
assert!(block_expr.get_block().is_some());
assert_eq!(block_expr.get_block().unwrap().len(), 1);
let lit = PureExpr::Lit("x".into());
assert!(lit.get_block().is_none());
}
}