mirror of
https://github.com/OMGeeky/appmap_tracing_test.git
synced 2025-12-27 06:29:32 +01:00
Get some basic tracing to work
Stuff working: - method calls with the tracing::instrument attribute get recorded and added to the AppMap (as classMap structure if not already there and as event) This results in the Trace View and the Dependency Map starting to work (not with many features yet but hey, better than nothing)
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
appmap_dir: maps/tmp/appmap
|
||||
appmap_dir: maps/tmp/
|
||||
language: rust
|
||||
name: appmap_tracing_test
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{info, instrument};
|
||||
|
||||
pub use event_id::EventId;
|
||||
|
||||
@@ -29,12 +30,17 @@ pub struct MessageCallObject {}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct AppMapObject {
|
||||
pub version: String,
|
||||
pub metadata: MetadataObject,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub metadata: Option<MetadataObject>,
|
||||
// pub class_map: Vec<CodeObject>,
|
||||
#[serde(rename = "classMap")]
|
||||
pub class_map: Vec<CodeObjectType>,
|
||||
pub events: Vec<EventObject>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
#[serde(rename = "eventUpdates")]
|
||||
pub event_updates: Option<HashMap<u32, EventObject>>,
|
||||
}
|
||||
//region events
|
||||
@@ -114,10 +120,11 @@ pub struct CallObject {
|
||||
#[serde(default)]
|
||||
pub parameters: Option<Vec<ParameterObject>>,
|
||||
///Required flag if the method is class-scoped (static) or instance-scoped. Must be true or false. Example: true.
|
||||
#[serde(rename = "static")]
|
||||
pub is_static: bool,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub data: CallObjectType,
|
||||
pub type_: CallObjectType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
@@ -125,7 +132,7 @@ pub struct CallObject {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum CallObjectType {
|
||||
Normal,
|
||||
Function(FunctionCallObject),
|
||||
Function,
|
||||
HttpServerRequest(HttpServerRequestCallObject),
|
||||
HttpServerResponse(HttpServerResponseCallObject),
|
||||
HttpClientRequest(HttpClientRequestCallObject),
|
||||
@@ -133,32 +140,6 @@ pub enum CallObjectType {
|
||||
SqlQuery(SqlQueryCallObject),
|
||||
Message(MessageCallObject),
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
/// Note:
|
||||
///
|
||||
/// In order to correlate function call events with function objects defined in the class map, the
|
||||
/// path and lineno attributes of each "call" event should exactly match the location attribute of
|
||||
/// the corresponding function in the classMap.
|
||||
pub struct FunctionCallObject {
|
||||
/// Recommended path name of the file which triggered the event.
|
||||
/// Example: "/src/architecture/lib/appland/local/client.rb".
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub path: Option<PathBuf>,
|
||||
/// Recommended line number which triggered the event. Example: 5.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub lineno: Option<usize>,
|
||||
/// Optional parameter object describing the object on which the function is called. Corresponds
|
||||
/// to the receiver, self and this concept found in various programming languages.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub receiver: Option<ParameterObject>,
|
||||
/// Recommended array of parameter objects describing the function call parameters.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub parameters: Option<Vec<ParameterObject>>,
|
||||
}
|
||||
//endregion
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
@@ -236,6 +217,7 @@ pub struct FunctionCodeObject {
|
||||
#[serde(default)]
|
||||
pub location: Option<String>,
|
||||
///Required flag if the method is class-scoped (static) or instance-scoped. Must be true or false. Example: true.
|
||||
#[serde(rename = "static")]
|
||||
pub is_static: bool,
|
||||
///Optional list of arbitrary labels describing the function.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -252,3 +234,8 @@ pub struct FunctionCodeObject {
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
#[instrument]
|
||||
pub fn test_sub_mod() {
|
||||
info!("test message from test_sub_mod");
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ use std::ops::{Deref, DerefMut};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct EventId(u32);
|
||||
pub struct EventId(u64);
|
||||
impl Deref for EventId {
|
||||
type Target = u32;
|
||||
type Target = u64;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
@@ -13,12 +13,12 @@ impl Deref for EventId {
|
||||
}
|
||||
impl DerefMut for EventId {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for EventId {
|
||||
fn from(value: u32) -> Self {
|
||||
impl From<u64> for EventId {
|
||||
fn from(value: u64) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,6 @@ impl Deref for ObjectId {
|
||||
}
|
||||
impl DerefMut for ObjectId {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
25
src/extensions.rs
Normal file
25
src/extensions.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
pub trait OptionVecExtensions<T> {
|
||||
fn push_or_create(&mut self, value: T);
|
||||
fn push_or_create_and_get_mut(&mut self, value: T) -> &mut T;
|
||||
fn push_or_create_and_get(&mut self, value: T) -> &T;
|
||||
}
|
||||
impl<T> OptionVecExtensions<T> for Option<Vec<T>> {
|
||||
fn push_or_create(&mut self, value: T) {
|
||||
if let Some(children) = self {
|
||||
children.push(value);
|
||||
} else {
|
||||
*self = Some(vec![value]);
|
||||
}
|
||||
}
|
||||
fn push_or_create_and_get_mut(&mut self, value: T) -> &mut T {
|
||||
self.push_or_create(value);
|
||||
let vec = self.as_mut().expect("We just created it");
|
||||
let i = vec.len();
|
||||
vec.get_mut(i).expect("This can not be empty")
|
||||
}
|
||||
fn push_or_create_and_get(&mut self, value: T) -> &T {
|
||||
self.push_or_create(value);
|
||||
let vec = self.as_ref().expect("We just created it");
|
||||
vec.get(vec.len()).expect("This can not be empty")
|
||||
}
|
||||
}
|
||||
254
src/lib.rs
254
src/lib.rs
@@ -1,16 +1,254 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::error::Error;
|
||||
use std::fmt::Debug;
|
||||
use std::time::Instant;
|
||||
use tracing::field::{Field, Visit};
|
||||
use tracing::span::{Attributes, Record};
|
||||
use tracing::{Id, Subscriber};
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{Event, Id, Subscriber};
|
||||
use tracing_subscriber::layer::Context;
|
||||
use tracing_subscriber::Layer;
|
||||
|
||||
use crate::appmap_definition::*;
|
||||
use crate::extensions::OptionVecExtensions;
|
||||
use crate::node_functions::*;
|
||||
|
||||
pub mod appmap_definition;
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
pub struct AppMap {
|
||||
#[serde(flatten)]
|
||||
pub data: crate::appmap_definition::AppMapObject,
|
||||
pub data: AppMapObject,
|
||||
#[serde(skip)]
|
||||
next_event_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AppMapLayer {
|
||||
pub test: Mutex<AppMap>,
|
||||
}
|
||||
|
||||
impl AppMapLayer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
test: Mutex::new(AppMap::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl AppMap {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: AppMapObject {
|
||||
version: "1.12".to_string(),
|
||||
metadata: None,
|
||||
class_map: vec![],
|
||||
events: vec![],
|
||||
event_updates: None,
|
||||
},
|
||||
next_event_id: 1,
|
||||
}
|
||||
}
|
||||
pub fn get_next_event_id(&mut self) -> u64 {
|
||||
let x = self.next_event_id;
|
||||
self.next_event_id += 1;
|
||||
x
|
||||
}
|
||||
|
||||
pub fn add_function_call_event(
|
||||
&mut self,
|
||||
thread_id: u32,
|
||||
class: String,
|
||||
method: String,
|
||||
path: Option<PathBuf>,
|
||||
lineno: Option<usize>,
|
||||
is_static: bool,
|
||||
) {
|
||||
let id = self.get_next_event_id();
|
||||
// let class_name = class.clone();
|
||||
// let class_name = class_name.rsplit_once("::").unwrap_or(("", &class)).1;
|
||||
self.data.events.push(EventObject {
|
||||
id: EventId::from(id),
|
||||
thread_id,
|
||||
event: EventObjectType::Call(CallObject {
|
||||
defined_class: class.to_string(),
|
||||
method_id: method.clone(),
|
||||
path: path.clone(),
|
||||
lineno,
|
||||
receiver: None,
|
||||
parameters: None,
|
||||
is_static,
|
||||
type_: CallObjectType::Function,
|
||||
}),
|
||||
});
|
||||
let existing_node = self.find_in_class_map(&class, &method);
|
||||
if existing_node.is_none() {
|
||||
println!("node not found: {} ; {}", class, method);
|
||||
self.add_func_to_hierarchy(
|
||||
class,
|
||||
method,
|
||||
path.map(|x| x.to_str().map(|x| format!("{}:{}", x, lineno.unwrap_or(0))))
|
||||
.flatten(),
|
||||
);
|
||||
} else {
|
||||
println!(
|
||||
"node already existing: {} ; {} => {:?}",
|
||||
class, method, existing_node
|
||||
);
|
||||
}
|
||||
}
|
||||
fn add_func_to_hierarchy(&mut self, class: String, method: String, path: Option<String>) {
|
||||
let func = FunctionCodeObject {
|
||||
name: method,
|
||||
location: path,
|
||||
is_static: true,
|
||||
labels: None,
|
||||
comment: None,
|
||||
source: None,
|
||||
};
|
||||
let func = CodeObjectType::Function(func);
|
||||
|
||||
let class_node = self.find_class_in_class_map_mut(&class);
|
||||
if let Some(class_node) = class_node {
|
||||
class_node.children.push_or_create(func);
|
||||
} else {
|
||||
self.add_class_to_hierarchy(&class);
|
||||
let class_node = self
|
||||
.find_class_in_class_map_mut(&class)
|
||||
.expect("We just created this node. It can not be None");
|
||||
class_node.children.push_or_create(func);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_class_to_hierarchy(&mut self, class: &str) {
|
||||
println!("class_map: {:?}", self.data.class_map);
|
||||
let class_parts = class.split_once("::");
|
||||
if let Some((base, name)) = class_parts {
|
||||
//class is a subclass. Check if the parent of the class exists already
|
||||
let mut parent_class = self.find_class_in_class_map_mut(base);
|
||||
if parent_class.is_none() {
|
||||
//parent did not exist. Create it!
|
||||
self.add_class_to_hierarchy(base);
|
||||
parent_class = self.find_class_in_class_map_mut(base);
|
||||
}
|
||||
let class_node = CodeObjectType::Class(ClassCodeObject {
|
||||
name: name.to_string(),
|
||||
children: None,
|
||||
});
|
||||
parent_class
|
||||
.expect("Could not find or create the parent class")
|
||||
.children
|
||||
.push_or_create(class_node);
|
||||
println!(
|
||||
"added sub class: {} under {} => {:?}",
|
||||
name, base, self.data.class_map
|
||||
);
|
||||
return;
|
||||
}
|
||||
//could not split so the class should be a top level class
|
||||
println!("got add request for top level class: {}", class);
|
||||
|
||||
let top_level_class = self.find_class_in_class_map_mut(class);
|
||||
if top_level_class.is_some() {
|
||||
return;
|
||||
} else {
|
||||
let class_node = CodeObjectType::Class(ClassCodeObject {
|
||||
name: class.to_string(),
|
||||
children: None,
|
||||
});
|
||||
|
||||
let classes: &mut Vec<_> = &mut self.data.class_map;
|
||||
classes.push(class_node);
|
||||
println!("Added top level class: {} => {:?}", class, classes);
|
||||
}
|
||||
}
|
||||
|
||||
fn find_class_in_class_map(&self, class: &str) -> Option<&ClassCodeObject> {
|
||||
for node in self.data.class_map.iter() {
|
||||
let class_node = find_class_in_tree(node, class);
|
||||
if class_node.is_some() {
|
||||
return class_node;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
fn find_in_class_map(&self, class: &str, method: &str) -> Option<&ClassCodeObject> {
|
||||
for node in self.data.class_map.iter() {
|
||||
let class_node = find_class_in_tree(node, class);
|
||||
if let Some(class_node) = class_node {
|
||||
if class_node.name == method {
|
||||
return Some(class_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
fn find_class_in_class_map_mut(&mut self, class: &str) -> Option<&mut ClassCodeObject> {
|
||||
for node in self.data.class_map.iter_mut() {
|
||||
let class_node = find_class_in_tree_mut(node, class);
|
||||
if class_node.is_some() {
|
||||
return class_node;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
fn find_in_class_map_mut(&mut self, class: &str, method: &str) -> Option<&mut CodeObjectType> {
|
||||
for node in self.data.class_map.iter_mut() {
|
||||
let class_node = find_class_in_tree_mut(node, class);
|
||||
if let Some(class_node) = class_node {
|
||||
if let Some(children) = class_node.children.as_mut() {
|
||||
for child in children.iter_mut() {
|
||||
let result = is_node_the_searched_function_mut(child, method);
|
||||
if result.is_some() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn write_to_file(&self) -> Result<(), Box<dyn Error>> {
|
||||
let s = serde_json::to_string_pretty(self)?;
|
||||
let mut file = File::options()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(Path::new("maps/tmp/test_1.appmap.json"))?;
|
||||
file.write_all(s.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Subscriber + Debug + for<'lookup> tracing_subscriber::registry::LookupSpan<'lookup>>
|
||||
Layer<S> for AppMapLayer
|
||||
{
|
||||
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
|
||||
println!("event: {:?}; ctx: {:?}", event, ctx);
|
||||
}
|
||||
fn on_enter(&self, id: &Id, ctx: Context<'_, S>) {
|
||||
println!("on_enter=> id: {:?}; ctx: {:?}", id, ctx);
|
||||
let metadata = ctx.metadata(id);
|
||||
if let Some(metadata) = metadata {
|
||||
let parameters = metadata.fields();
|
||||
println!("parameters: {:?}", parameters);
|
||||
let x = metadata.module_path().unwrap() == metadata.target();
|
||||
println!("some test data: {:?}", x);
|
||||
|
||||
self.test.lock().unwrap().add_function_call_event(
|
||||
9999,
|
||||
metadata.target().to_string(),
|
||||
metadata.name().to_string(),
|
||||
metadata.file().map(|f| PathBuf::from(f)),
|
||||
metadata.line().map(|x| x as usize),
|
||||
true,
|
||||
);
|
||||
self.test.lock().unwrap().write_to_file().unwrap();
|
||||
}
|
||||
}
|
||||
fn on_close(&self, id: Id, ctx: Context<'_, S>) {
|
||||
println!("on_close=> id: {:?}; ctx: {:?}", id, ctx);
|
||||
}
|
||||
}
|
||||
mod extensions;
|
||||
mod node_functions;
|
||||
|
||||
130
src/main.rs
130
src/main.rs
@@ -1,78 +1,94 @@
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::fmt::Error as FmtError;
|
||||
use std::path::PathBuf;
|
||||
use tracing::{info, instrument};
|
||||
|
||||
use appmap_tracing_test::appmap_definition::{
|
||||
AppMapObject, CallObject, CallObjectType, ClassCodeObject, CodeObject, CodeObjectType, EventId,
|
||||
EventObject, EventObjectType, FunctionCallObject, FunctionCodeObject, MetadataObject,
|
||||
PackageCodeObject,
|
||||
};
|
||||
use appmap_tracing_test::AppMap;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::Registry;
|
||||
|
||||
use appmap_tracing_test::appmap_definition::*;
|
||||
use appmap_tracing_test::*;
|
||||
|
||||
pub fn main() -> Result<(), Box<dyn Error>> {
|
||||
init_tracing();
|
||||
|
||||
sample_json()?;
|
||||
test_sub_mod();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_tracing() {
|
||||
// let stdout_layer = tracing_subscriber::fmt::layer().pretty();
|
||||
let app_layer = AppMapLayer::new();
|
||||
|
||||
let subscriber = Registry::default()
|
||||
//
|
||||
// .with(stdout_layer)
|
||||
//
|
||||
.with(app_layer);
|
||||
|
||||
tracing::subscriber::set_global_default(subscriber).expect("Unable to set global subscriber");
|
||||
}
|
||||
|
||||
//region AppMapObject
|
||||
#[instrument]
|
||||
fn sample_json() -> Result<(), Box<dyn Error>> {
|
||||
let data = AppMap {
|
||||
data: AppMapObject {
|
||||
metadata: MetadataObject {},
|
||||
class_map: vec![CodeObjectType::Package(PackageCodeObject {
|
||||
name: "main pkg".to_string(),
|
||||
children: Some(vec![CodeObjectType::Class(ClassCodeObject {
|
||||
name: "main cls".to_string(),
|
||||
children: Some(vec![CodeObjectType::Function(FunctionCodeObject {
|
||||
name: "sample_json".to_string(),
|
||||
location: Some(format!(
|
||||
"{}:{}",
|
||||
PathBuf::from("src/main.rs").to_str().ok_or(FmtError)?,
|
||||
14
|
||||
)),
|
||||
is_static: true,
|
||||
labels: Some(vec!["security".to_string()]),
|
||||
comment: None,
|
||||
source: None,
|
||||
})]),
|
||||
})]),
|
||||
})],
|
||||
events: vec![EventObject {
|
||||
id: EventId::from(1),
|
||||
thread_id: 9999,
|
||||
event: EventObjectType::Call(CallObject {
|
||||
defined_class: "main".to_string(),
|
||||
method_id: "sample_json".to_string(),
|
||||
path: Some(PathBuf::from("src/main.rs")),
|
||||
lineno: Some(14),
|
||||
receiver: None,
|
||||
parameters: None,
|
||||
info!("creating sample object");
|
||||
let data = AppMapObject {
|
||||
metadata: None,
|
||||
class_map: vec![CodeObjectType::Package(PackageCodeObject {
|
||||
name: "main pkg".to_string(),
|
||||
children: Some(vec![CodeObjectType::Class(ClassCodeObject {
|
||||
name: "main cls".to_string(),
|
||||
children: Some(vec![CodeObjectType::Function(FunctionCodeObject {
|
||||
name: "sample_json".to_string(),
|
||||
location: Some(format!(
|
||||
"{}:{}",
|
||||
PathBuf::from("src/main.rs").to_str().ok_or(FmtError)?,
|
||||
14
|
||||
)),
|
||||
is_static: true,
|
||||
data: CallObjectType::Function(FunctionCallObject {
|
||||
path: None,
|
||||
lineno: None,
|
||||
receiver: None,
|
||||
parameters: None,
|
||||
}),
|
||||
}),
|
||||
}],
|
||||
version: String::from("1.12"),
|
||||
event_updates: None,
|
||||
},
|
||||
labels: Some(vec!["security".to_string()]),
|
||||
comment: None,
|
||||
source: None,
|
||||
})]),
|
||||
})]),
|
||||
})],
|
||||
events: vec![EventObject {
|
||||
id: EventId::from(1),
|
||||
thread_id: 9999,
|
||||
event: EventObjectType::Call(CallObject {
|
||||
defined_class: "main".to_string(),
|
||||
method_id: "sample_json".to_string(),
|
||||
path: Some(PathBuf::from("src/main.rs")),
|
||||
lineno: Some(14),
|
||||
receiver: None,
|
||||
parameters: None,
|
||||
is_static: true,
|
||||
type_: CallObjectType::Function,
|
||||
}),
|
||||
}],
|
||||
version: String::from("1.12"),
|
||||
event_updates: None,
|
||||
};
|
||||
|
||||
println!("data debug: {:?}", data);
|
||||
info!("data debug: {:?}", data);
|
||||
let data_string = serde_json::to_string_pretty(&data)?;
|
||||
println!("data to string: {}", data_string);
|
||||
info!("data to string: {}", data_string);
|
||||
// println!("data to string: {}", data_string);
|
||||
|
||||
let data_reversed: AppMap = sample_from_str(&data_string)?;
|
||||
println!("data_reversed debug: {:?}", data_reversed);
|
||||
let data_reversed: AppMapObject = sample_from_str(&data_string)?;
|
||||
info!("data_reversed debug: {:?}", data_reversed);
|
||||
assert_eq!(data, data_reversed);
|
||||
println!("it works!");
|
||||
info!("it works!");
|
||||
// println!("it works!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn sample_from_str(s: &str) -> Result<AppMap, Box<dyn Error>> {
|
||||
let data_reversed: AppMap = serde_json::from_str(s)?;
|
||||
println!("sample_from_str(s): result debug: {:?}", data_reversed);
|
||||
#[instrument]
|
||||
fn sample_from_str(s: &str) -> Result<AppMapObject, Box<dyn Error>> {
|
||||
let data_reversed: AppMapObject = serde_json::from_str(s)?;
|
||||
info!("sample_from_str(s): result debug: {:?}", data_reversed);
|
||||
Ok(data_reversed)
|
||||
}
|
||||
//endregion
|
||||
|
||||
85
src/node_functions/mod.rs
Normal file
85
src/node_functions/mod.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
use crate::appmap_definition::*;
|
||||
|
||||
pub fn find_class_in_tree<'a>(
|
||||
node: &'a CodeObjectType,
|
||||
class: &str,
|
||||
) -> Option<&'a ClassCodeObject> {
|
||||
let children = match node {
|
||||
CodeObjectType::Class(c) => {
|
||||
if class.ends_with(&c.name) {
|
||||
return Some(c);
|
||||
}
|
||||
if class.starts_with(&c.name) {
|
||||
c.children.as_ref()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
if let Some(children) = children {
|
||||
for x in children.iter() {
|
||||
let child_found = find_class_in_tree(x, class);
|
||||
if child_found.is_some() {
|
||||
return child_found;
|
||||
}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
pub fn is_node_the_searched_function<'a>(
|
||||
node: &'a CodeObjectType,
|
||||
method: &str,
|
||||
) -> Option<&'a CodeObjectType> {
|
||||
match node {
|
||||
CodeObjectType::Function(f) => {
|
||||
if f.name == method {
|
||||
return Some(node);
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn find_class_in_tree_mut<'a>(
|
||||
node: &'a mut CodeObjectType,
|
||||
class: &str,
|
||||
) -> Option<&'a mut ClassCodeObject> {
|
||||
println!("trying to find class: {} in node: {:?}", class, node);
|
||||
let children = match node {
|
||||
CodeObjectType::Class(c) => {
|
||||
if class.ends_with(&c.name) {
|
||||
return Some(c);
|
||||
}
|
||||
if class.starts_with(&c.name) {
|
||||
c.children.as_mut()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
if let Some(children) = children {
|
||||
for x in children.iter_mut() {
|
||||
let child_found = find_class_in_tree_mut(x, class);
|
||||
if child_found.is_some() {
|
||||
return child_found;
|
||||
}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
pub fn is_node_the_searched_function_mut<'a>(
|
||||
node: &'a mut CodeObjectType,
|
||||
method: &str,
|
||||
) -> Option<&'a mut CodeObjectType> {
|
||||
match node {
|
||||
CodeObjectType::Function(f) => {
|
||||
if f.name == method {
|
||||
return Some(node);
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user