<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>html, body {
margin: 0;
padding: 0;
}
.app {
margin: 10px;
padding: 0;
}
.files-list {
margin: 10px 0 0;
width: 100%;
border-collapse: collapse;
}
.files-list__head {
border: 1px solid #999;
}
.files-list__head > tr > th {
padding: 10px;
border: 1px solid #999;
text-align: left;
font-weight: normal;
background: #ddd;
}
.files-list__body {
}
.files-list__file {
cursor: pointer;
}
.files-list__file:hover {
background: #ccf;
}
.files-list__file > td {
padding: 10px;
border: 1px solid #999;
}
.files-list__file > td:first-child::before {
content: '\01F4C4';
margin-right: 1em;
}
.files-list__file_low {
background: #fcc;
}
.files-list__file_medium {
background: #ffc;
}
.files-list__file_high {
background: #cfc;
}
.files-list__file_folder > td:first-child::before {
content: '\01F4C1';
margin-right: 1em;
}
.file-header {
border: 1px solid #999;
display: flex;
justify-content: space-between;
align-items: center;
}
.file-header__back {
margin: 10px;
cursor: pointer;
flex-shrink: 0;
flex-grow: 0;
text-decoration: underline;
color: #338;
}
.file-header__name {
margin: 10px;
flex-shrink: 2;
flex-grow: 2;
}
.file-header__stat {
margin: 10px;
flex-shrink: 0;
flex-grow: 0;
}
.file-content {
margin: 10px 0 0;
border: 1px solid #999;
padding: 10px;
}
.code-line {
margin: 0;
padding: 0.3em;
height: 1em;
}
.code-line_covered {
background: #cfc;
}
.code-line_uncovered {
background: #fcc;
}
</style>
</head>
<body>
<div id="root"></div>
<script>var data = {"files":[{"path":["/","home","wcampbell","projects","wcampbell","code","saleae","src","client.rs"],"content":"//! This module defines the client data structure - the main entry point of communication\n//! to the saleae\n\nuse anyhow::Result;\nuse std::io::prelude::{Read, Write};\nuse std::io::{BufReader, BufWriter};\nuse std::net::TcpStream;\n\nuse crate::device::ConnectedDevice;\nuse crate::performance::PerformanceOption;\nuse crate::request::Request;\nuse crate::response::Response;\nuse crate::samplerate::SampleRate;\n\n#[faux::create]\n#[derive(Debug)]\npub struct Connection {\n stream: TcpStream,\n}\n\n#[faux::methods]\nimpl Connection {\n pub fn new(ip_port: \u0026str) -\u003e Self {\n Connection {\n stream: TcpStream::connect(ip_port).unwrap(),\n }\n }\n\n pub fn general_recieve_ack(\u0026mut self) -\u003e Result\u003cbool\u003e {\n let r: String = std::str::from_utf8(\u0026self.read_line()?)?.to_string();\n Ok(Response::verify_ack(\u0026r))\n }\n\n pub fn general_recieve_message(\u0026mut self) -\u003e Result\u003cString\u003e {\n let msg: String = std::str::from_utf8(\u0026self.read_line()?)?.to_string();\n Response::verify_ack(\u0026msg);\n Ok(msg)\n }\n\n fn read_line(\u0026mut self) -\u003e Result\u003cVec\u003cu8\u003e\u003e {\n let mut reader = BufReader::new(\u0026self.stream);\n let mut buf = [0; 500];\n let len = reader.read(\u0026mut buf)?;\n if len \u003c 1 {\n panic!(\"read buffer len \u003c 0\");\n }\n Ok(buf[..len].to_vec())\n }\n\n //TODO Support for parameters\n pub fn run_command(\u0026mut self, command: \u0026str) -\u003e Result\u003c()\u003e {\n let mut writer = BufWriter::new(\u0026self.stream);\n let len = writer.write(command.as_bytes()).unwrap();\n if len \u003c 1 {\n panic!(\"write buffer len \u003c 0\");\n }\n Ok(())\n }\n}\n\n#[derive(Debug)]\n/// Main interface for communication to Saleae Logic\npub struct Client {\n /// tcp stream with connection to saleae\n connection: Connection,\n}\n\n/// Constructor\nimpl Client {\n /// constructor\n //TODO make this create a connection from a string\n pub fn new(connection: Connection) -\u003e Result\u003cClient\u003e {\n Ok(Client { connection })\n }\n}\n\n/// Interface for setting and getting Logic information\nimpl Client {\n /// Set a trigger of channels\n /// TODO create trigger methods/structs/tests\n\n pub fn set_num_samples(\u0026mut self, num: u32) -\u003e Result\u003cbool\u003e {\n self.connection\n .run_command(\u0026format!(\"set_num_samples, {}\\0\", num))?;\n Ok(self.connection.general_recieve_ack()?)\n }\n\n pub fn get_num_samples(\u0026mut self) -\u003e Result\u003cu32\u003e {\n self.connection.run_command(\"get_num_samples\\0\")?;\n let response = self.connection.general_recieve_message()?;\n if Response::verify_ack(\u0026response) {\n Ok(Response::parse_num_samples(\u0026Response::remove_ack(\n \u0026response,\n )))\n } else {\n Err(anyhow!(\"No ACK found\"))\n }\n }\n\n /// Set the capture duration to a length of time\n pub fn set_capture_seconds(\u0026mut self, seconds: f32) -\u003e Result\u003cbool\u003e {\n self.connection\n .run_command(\u0026format!(\"set_capture_seconds, {}\\0\", seconds))?;\n Ok(self.connection.general_recieve_ack()?)\n }\n\n /// Set sample rate of saleae\n ///\n /// Note: Make sure to run `get_all_sample_rates` and set it from a available\n /// sample rate\n pub fn set_sample_rate(\u0026mut self, rate: \u0026SampleRate) -\u003e Result\u003cbool\u003e {\n self.connection.run_command(\u0026format!(\n \"set_sample_rate, {}, {}\\0\",\n rate.DigitalSampleRate, rate.AnalogSampleRate\n ))?;\n Ok(self.connection.general_recieve_ack()?)\n }\n\n pub fn get_sample_rate(\u0026mut self) -\u003e Result\u003cSampleRate\u003e {\n self.connection.run_command(\"get_sample_rate\\0\")?;\n let response = self.connection.general_recieve_message()?;\n Ok(Response::parse_get_sample_rate(\u0026Response::remove_ack(\n \u0026response,\n )))\n }\n\n pub fn get_all_sample_rates(\u0026mut self) -\u003e Result\u003cVec\u003cSampleRate\u003e\u003e {\n self.connection.run_command(\"get_all_sample_rates\\0\")?;\n let response = self.connection.general_recieve_message()?;\n Ok(Response::parse_get_all_sample_rates(\u0026Response::remove_ack(\n \u0026response,\n )))\n }\n\n /// Return current performance level of Logic\n pub fn get_performance(\u0026mut self) -\u003e Result\u003cu8\u003e {\n self.connection.run_command(\"get_performance\\0\")?;\n let response = self.connection.general_recieve_message()?;\n Ok(Response::parse_performance(\u0026Response::remove_ack(\n \u0026response,\n )))\n }\n\n /// Set the performance value, controlling the USB traffic and quality\n pub fn set_performance(\u0026mut self, perf: PerformanceOption) -\u003e Result\u003cbool\u003e {\n let input = String::from(\u0026format!(\"set_performance, {}\\0\", perf as i32));\n self.connection.run_command(\u0026input)?;\n Ok(self.connection.general_recieve_ack()?)\n }\n\n //TODO get_capture_pretrigger_buffer_size\n\n /// Return current connected devices of Logic\n pub fn get_connected_devices(\u0026mut self) -\u003e Result\u003cVec\u003cConnectedDevice\u003e\u003e {\n self.connection.run_command(\"get_connected_devices\\0\")?;\n let response = self.connection.general_recieve_message()?;\n Ok(Response::parse_connected_devices(\u0026Response::remove_ack(\n \u0026response,\n )))\n }\n\n /// Find index of device from the list of devices connected to Saleae\n ///\n /// Get current index of connected devices and find equal device to parameter.\n /// Send that device as active device to logic.\n ///\n /// Note: Indices start at 1, not 0\n /// TODO: test with multiple saleae\n pub fn select_active_device(\u0026mut self, device: ConnectedDevice) -\u003e Result\u003cbool\u003e {\n let b = self\n .get_connected_devices()\n .unwrap()\n .into_iter()\n .position(|a| a == device);\n self.connection\n .run_command(\u0026format!(\"select_active_device, {}\", b.unwrap() + 1))?;\n // Weirdly doesn't return an ACK\n Ok(true)\n }\n\n /// Return current active device of Logic\n pub fn get_active_device(\u0026mut self) -\u003e Result\u003cConnectedDevice\u003e {\n self.connection.run_command(\"get_connected_devices\\0\")?;\n let response = self.connection.general_recieve_message()?;\n Ok(\n Response::parse_connected_devices(\u0026Response::remove_ack(\u0026response))\n .into_iter()\n .find(|a| a.is_active)\n .unwrap(),\n )\n }\n\n /// Parse the get active channels command into tuples of digital and analog\n /// channels that are current\n pub fn get_active_channels(\u0026mut self) -\u003e Result\u003cVec\u003cVec\u003cu8\u003e\u003e\u003e {\n self.connection.run_command(\"get_active_channels\\0\")?;\n let response = self.connection.general_recieve_message()?;\n Ok(Response::parse_get_active_channels(\u0026Response::remove_ack(\n \u0026response,\n ))?)\n }\n\n /// Set the active channels for the Logic program\n ///\n /// # Example\n /// TODO add get_active_channels\n pub fn set_active_channels(\n \u0026mut self,\n digital_channels: \u0026[u8],\n analog_channels: \u0026[u8],\n ) -\u003e Result\u003cbool\u003e {\n self.connection\n .run_command(\u0026Request::prepare_set_active_channels(\n \u0026digital_channels,\n \u0026analog_channels,\n )?)?;\n Ok(self.connection.general_recieve_ack()?)\n }\n\n /// Reset Active Channel\n pub fn reset_active_channels(\u0026mut self) -\u003e Result\u003cbool\u003e {\n self.connection.run_command(\"reset_active_channels\\0\")?;\n Ok(self.connection.general_recieve_ack()?)\n }\n\n //TODO get_digital_voltage_options OR get_full_scale_voltage_range\n //TODO set_full_scale_voltage_range\n\n /// Start Capture, without wating for ack/nack\n pub fn start_capture(\u0026mut self) -\u003e Result\u003cbool\u003e {\n self.connection.run_command(\"capture\\0\")?;\n Ok(true)\n // Doesn't return ACK\n }\n\n /// Start Capture, then wait until ack\n pub fn start_capture_block_until_finished(\u0026mut self) -\u003e Result\u003cbool\u003e {\n self.start_capture()?;\n Ok(self.connection.general_recieve_ack()?)\n }\n\n /// Check if processing is complete\n pub fn is_processing_complete(\u0026mut self) -\u003e Result\u003cbool\u003e {\n self.connection.run_command(\"is_processing_complete\\0\")?;\n let response = self.connection.general_recieve_message()?;\n Ok(Response::parse_processing_complete(\u0026Response::remove_ack(\n \u0026response,\n )))\n }\n\n /// Stop the saleae capture\n pub fn stop_capture(\u0026mut self) -\u003e Result\u003cbool\u003e {\n self.connection.run_command(\"stop_capture\\0\")?;\n Ok(self.connection.general_recieve_ack()?)\n }\n\n //TODO capture_to_file\n //TODO save_to_file\n //TODO load_from_file\n\n /// Close all tabs\n pub fn close_all_tabs(\u0026mut self) -\u003e Result\u003cbool\u003e {\n self.connection.run_command(\"close_all_tabs\\0\")?;\n Ok(self.connection.general_recieve_ack()?)\n }\n\n //TODO export_data2\n //TODO get_analyzers\n //TODO export_analyzer\n //TODO is_analyzer_complete\n //TODO get_capture_range\n //TODO get_viewstate\n //TODO get_viewstate\n //TODO exit\n}\n","traces":[{"line":23,"address":4253312,"length":1,"stats":{"Line":0}},{"line":25,"address":4253326,"length":1,"stats":{"Line":0}},{"line":29,"address":4253392,"length":1,"stats":{"Line":0}},{"line":30,"address":4253407,"length":1,"stats":{"Line":0}},{"line":31,"address":4254006,"length":1,"stats":{"Line":0}},{"line":34,"address":4254512,"length":1,"stats":{"Line":0}},{"line":35,"address":4254527,"length":1,"stats":{"Line":0}},{"line":36,"address":4255117,"length":1,"stats":{"Line":0}},{"line":37,"address":4255160,"length":1,"stats":{"Line":0}},{"line":40,"address":null,"length":0,"stats":{"Line":0}},{"line":41,"address":4255695,"length":1,"stats":{"Line":0}},{"line":42,"address":4255762,"length":1,"stats":{"Line":0}},{"line":43,"address":4255819,"length":1,"stats":{"Line":0}},{"line":44,"address":4256050,"length":1,"stats":{"Line":0}},{"line":45,"address":4256119,"length":1,"stats":{"Line":0}},{"line":47,"address":4256061,"length":1,"stats":{"Line":0}},{"line":51,"address":4256336,"length":1,"stats":{"Line":0}},{"line":52,"address":4256358,"length":1,"stats":{"Line":0}},{"line":53,"address":4256387,"length":1,"stats":{"Line":0}},{"line":54,"address":4256545,"length":1,"stats":{"Line":0}},{"line":55,"address":4256565,"length":1,"stats":{"Line":0}},{"line":57,"address":null,"length":0,"stats":{"Line":0}},{"line":72,"address":4233664,"length":1,"stats":{"Line":6}},{"line":73,"address":4233674,"length":1,"stats":{"Line":6}},{"line":82,"address":4233808,"length":1,"stats":{"Line":1}},{"line":83,"address":4233834,"length":1,"stats":{"Line":1}},{"line":84,"address":4233839,"length":1,"stats":{"Line":1}},{"line":85,"address":4234352,"length":1,"stats":{"Line":1}},{"line":88,"address":4234720,"length":1,"stats":{"Line":2}},{"line":89,"address":4234742,"length":1,"stats":{"Line":2}},{"line":90,"address":4234993,"length":1,"stats":{"Line":2}},{"line":91,"address":4235286,"length":1,"stats":{"Line":2}},{"line":92,"address":4235414,"length":1,"stats":{"Line":1}},{"line":93,"address":4235379,"length":1,"stats":{"Line":1}},{"line":96,"address":4235344,"length":1,"stats":{"Line":1}},{"line":101,"address":4235776,"length":1,"stats":{"Line":1}},{"line":102,"address":4235804,"length":1,"stats":{"Line":1}},{"line":103,"address":4235809,"length":1,"stats":{"Line":1}},{"line":104,"address":4236321,"length":1,"stats":{"Line":1}},{"line":111,"address":4236688,"length":1,"stats":{"Line":1}},{"line":112,"address":4236718,"length":1,"stats":{"Line":1}},{"line":113,"address":4236723,"length":1,"stats":{"Line":1}},{"line":114,"address":4236730,"length":1,"stats":{"Line":1}},{"line":116,"address":4237352,"length":1,"stats":{"Line":1}},{"line":119,"address":4237728,"length":1,"stats":{"Line":1}},{"line":120,"address":4237750,"length":1,"stats":{"Line":1}},{"line":121,"address":4237973,"length":1,"stats":{"Line":1}},{"line":122,"address":4238305,"length":1,"stats":{"Line":1}},{"line":123,"address":4238260,"length":1,"stats":{"Line":1}},{"line":127,"address":4238688,"length":1,"stats":{"Line":0}},{"line":128,"address":4238710,"length":1,"stats":{"Line":0}},{"line":129,"address":4238933,"length":1,"stats":{"Line":0}},{"line":130,"address":4239262,"length":1,"stats":{"Line":0}},{"line":131,"address":4239217,"length":1,"stats":{"Line":0}},{"line":136,"address":4239648,"length":1,"stats":{"Line":1}},{"line":137,"address":4239670,"length":1,"stats":{"Line":1}},{"line":138,"address":4239893,"length":1,"stats":{"Line":1}},{"line":139,"address":4240225,"length":1,"stats":{"Line":1}},{"line":140,"address":4240180,"length":1,"stats":{"Line":1}},{"line":145,"address":4240592,"length":1,"stats":{"Line":1}},{"line":146,"address":4240618,"length":1,"stats":{"Line":1}},{"line":147,"address":4240941,"length":1,"stats":{"Line":1}},{"line":148,"address":4241221,"length":1,"stats":{"Line":1}},{"line":154,"address":4241760,"length":1,"stats":{"Line":1}},{"line":155,"address":4241782,"length":1,"stats":{"Line":1}},{"line":156,"address":4242005,"length":1,"stats":{"Line":1}},{"line":157,"address":4242334,"length":1,"stats":{"Line":1}},{"line":158,"address":4242289,"length":1,"stats":{"Line":1}},{"line":169,"address":4242720,"length":1,"stats":{"Line":0}},{"line":170,"address":4242738,"length":1,"stats":{"Line":0}},{"line":171,"address":null,"length":0,"stats":{"Line":0}},{"line":172,"address":null,"length":0,"stats":{"Line":0}},{"line":173,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":4230688,"length":1,"stats":{"Line":0}},{"line":175,"address":4242973,"length":1,"stats":{"Line":0}},{"line":176,"address":4242981,"length":1,"stats":{"Line":0}},{"line":182,"address":4243792,"length":1,"stats":{"Line":0}},{"line":183,"address":4243814,"length":1,"stats":{"Line":0}},{"line":184,"address":4244037,"length":1,"stats":{"Line":0}},{"line":186,"address":4244321,"length":1,"stats":{"Line":0}},{"line":187,"address":null,"length":0,"stats":{"Line":0}},{"line":188,"address":4230800,"length":1,"stats":{"Line":0}},{"line":189,"address":null,"length":0,"stats":{"Line":0}},{"line":195,"address":4244944,"length":1,"stats":{"Line":1}},{"line":196,"address":4244966,"length":1,"stats":{"Line":1}},{"line":197,"address":4245205,"length":1,"stats":{"Line":1}},{"line":198,"address":4245537,"length":1,"stats":{"Line":1}},{"line":199,"address":4245492,"length":1,"stats":{"Line":1}},{"line":207,"address":4246448,"length":1,"stats":{"Line":1}},{"line":212,"address":4246483,"length":1,"stats":{"Line":1}},{"line":213,"address":4246524,"length":1,"stats":{"Line":1}},{"line":214,"address":4246504,"length":1,"stats":{"Line":1}},{"line":215,"address":4246514,"length":1,"stats":{"Line":1}},{"line":217,"address":4247085,"length":1,"stats":{"Line":1}},{"line":221,"address":4247760,"length":1,"stats":{"Line":1}},{"line":222,"address":4247782,"length":1,"stats":{"Line":1}},{"line":223,"address":4248002,"length":1,"stats":{"Line":1}},{"line":230,"address":4248288,"length":1,"stats":{"Line":1}},{"line":231,"address":4248307,"length":1,"stats":{"Line":1}},{"line":237,"address":4248576,"length":1,"stats":{"Line":1}},{"line":238,"address":4248591,"length":1,"stats":{"Line":1}},{"line":239,"address":4248794,"length":1,"stats":{"Line":1}},{"line":243,"address":4249088,"length":1,"stats":{"Line":1}},{"line":244,"address":4249110,"length":1,"stats":{"Line":1}},{"line":245,"address":4249333,"length":1,"stats":{"Line":1}},{"line":246,"address":4249665,"length":1,"stats":{"Line":1}},{"line":247,"address":4249620,"length":1,"stats":{"Line":1}},{"line":252,"address":4250032,"length":1,"stats":{"Line":1}},{"line":253,"address":4250054,"length":1,"stats":{"Line":1}},{"line":254,"address":4250274,"length":1,"stats":{"Line":1}},{"line":262,"address":4250560,"length":1,"stats":{"Line":1}},{"line":263,"address":4250582,"length":1,"stats":{"Line":1}},{"line":264,"address":4250802,"length":1,"stats":{"Line":1}}],"covered":71,"coverable":113},{"path":["/","home","wcampbell","projects","wcampbell","code","saleae","src","device.rs"],"content":"//! This module defines devices that are connected to the saleae logic program.\n//!\n//! ## Example of response from Logic\n//! ```text\n//! 1, Logic Pro 16, LOGIC_PRO_16_DEVICE, 0xdf03c43d1f3aa2f3, ACTIVE\n//! ```\n//! ## C# struct\n//! ```text\n//! struct ConnectedDevices\n//! {\n//! String type;\n//! String name;\n//! int device_id;\n//! int index;\n//! bool is_active;\n//! }\n//! ```\n\nuse anyhow::Result;\nuse std::str::FromStr;\n\ncustom_derive! {\n /// Device id for saleae devices\n #[allow(non_camel_case_types)]\n #[derive(Debug, EnumFromStr, PartialEq)]\n pub enum DeviceID {\n /// Regular 4 Wire device\n LOGIC_4_DEVICE,\n /// Regular 8 Wire device\n LOGIC_8_DEVICE,\n /// Pro 8 Wire device\n LOGIC_PRO_8_DEVICE,\n /// Pro 16 Wire device\n LOGIC_PRO_16_DEVICE,\n }\n}\n\n/// Connected Device to saleae, the main usability comes from the fromStr trait.\n#[derive(Debug, PartialEq)]\npub struct ConnectedDevice {\n /// type of device\n pub d_type: String,\n /// name of device\n pub name: String,\n /// id of device\n pub device_id: DeviceID,\n /// index of device\n pub index: String,\n /// if device is active\n pub is_active: bool,\n}\n\nimpl FromStr for ConnectedDevice {\n type Err = std::num::ParseIntError;\n fn from_str(response: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n let v: Vec\u003c\u0026str\u003e = response.split(',').map(|a| a.trim_start()).collect();\n\n /* parse into device_id */\n let device_id: DeviceID = v[2].parse().unwrap();\n\n /*\n * last element is ACTIVE, if that element doesn't don't cause a panic by\n * checking that element\n */\n let is_active = v.len() == 5 \u0026\u0026 v[4] == \"ACTIVE\";\n\n Ok(ConnectedDevice {\n d_type: v[0].to_string(),\n name: v[1].to_string(),\n device_id,\n index: v[3].to_string(),\n is_active,\n })\n }\n}\n","traces":[{"line":55,"address":4227568,"length":1,"stats":{"Line":3}},{"line":56,"address":4227588,"length":1,"stats":{"Line":6}},{"line":59,"address":4227696,"length":1,"stats":{"Line":3}},{"line":65,"address":4227784,"length":1,"stats":{"Line":3}},{"line":67,"address":4228081,"length":1,"stats":{"Line":3}},{"line":68,"address":4227847,"length":1,"stats":{"Line":3}},{"line":69,"address":4227942,"length":1,"stats":{"Line":3}},{"line":70,"address":4227999,"length":1,"stats":{"Line":3}},{"line":71,"address":4228020,"length":1,"stats":{"Line":3}},{"line":72,"address":4228074,"length":1,"stats":{"Line":3}}],"covered":10,"coverable":10},{"path":["/","home","wcampbell","projects","wcampbell","code","saleae","src","request.rs"],"content":"//! This module helps create requests to the Saleae Logic software\n//!\n//! The function here parse and type check the input from the API into strings to send into\n//! the Saleae socket\nuse anyhow::Result;\n\npub struct Request {}\n\nimpl Request {\n pub fn prepare_set_active_channels(\n digital_channels: \u0026[u8],\n analog_channels: \u0026[u8],\n ) -\u003e Result\u003cString\u003e {\n // Check only one kind of empty\n if digital_channels.is_empty() \u0026\u0026 analog_channels.is_empty() {\n return Err(anyhow!(\n \"Logic requires at least one active channel, no active channels found\"\n ));\n }\n\n let d_str = if !digital_channels.is_empty() {\n format!(\n \", digital_channels, {}\",\n Request::create_channel_str(digital_channels)?\n )\n } else {\n \"\".to_string()\n };\n\n let a_str = if !analog_channels.is_empty() {\n format!(\n \", analog_channels, {}\",\n Request::create_channel_str(analog_channels)?\n )\n } else {\n \"\".to_string()\n };\n\n Ok(format!(\"set_active_channels{}{}\\0\", d_str, a_str))\n }\n}\n\n/// Helper functions\nimpl Request {\n pub fn create_channel_str(v: \u0026[u8]) -\u003e Result\u003cString\u003e {\n let s = v\n .iter()\n .map(|a| format!(\"{}, \", a.to_string()))\n .collect::\u003cString\u003e();\n Ok(s[..s.len() - 2].to_string())\n }\n}\n","traces":[{"line":10,"address":4286880,"length":1,"stats":{"Line":2}},{"line":15,"address":4286922,"length":1,"stats":{"Line":2}},{"line":16,"address":4287143,"length":1,"stats":{"Line":1}},{"line":17,"address":null,"length":0,"stats":{"Line":0}},{"line":21,"address":4287106,"length":1,"stats":{"Line":2}},{"line":22,"address":4287351,"length":1,"stats":{"Line":0}},{"line":23,"address":4287253,"length":1,"stats":{"Line":2}},{"line":24,"address":4287260,"length":1,"stats":{"Line":2}},{"line":27,"address":4287228,"length":1,"stats":{"Line":1}},{"line":30,"address":4287758,"length":1,"stats":{"Line":2}},{"line":31,"address":4287946,"length":1,"stats":{"Line":0}},{"line":32,"address":4287844,"length":1,"stats":{"Line":2}},{"line":33,"address":4287851,"length":1,"stats":{"Line":2}},{"line":36,"address":4287815,"length":1,"stats":{"Line":1}},{"line":39,"address":4288349,"length":1,"stats":{"Line":2}},{"line":45,"address":4289584,"length":1,"stats":{"Line":2}},{"line":46,"address":4289604,"length":1,"stats":{"Line":2}},{"line":47,"address":null,"length":0,"stats":{"Line":0}},{"line":48,"address":4232032,"length":1,"stats":{"Line":2}},{"line":49,"address":null,"length":0,"stats":{"Line":0}},{"line":50,"address":4289710,"length":1,"stats":{"Line":2}}],"covered":16,"coverable":21},{"path":["/","home","wcampbell","projects","wcampbell","code","saleae","src","response.rs"],"content":"//! This module help discern and parse the responses from saleae\n\nuse crate::device::ConnectedDevice;\nuse crate::samplerate::SampleRate;\nuse anyhow::Result;\nuse std::str::FromStr;\n\n/// struct to handle responses\npub struct Response {}\n\n//TODO add errors\nimpl Response {\n /// Return string without \"\\nACK\" string line nor extra 0 char's from buffer\n pub fn remove_ack(response: \u0026str) -\u003e String {\n response\n .trim_end_matches(char::from(0))\n .trim_end_matches(\"\\nACK\")\n .to_string()\n }\n\n /// Check if last string is ACK\n pub fn verify_ack(response: \u0026str) -\u003e bool {\n response.lines().last().unwrap() == \"ACK\"\n }\n\n /// Parse the performance response\n ///\n /// # Sample Input\n /// The following values are expected as input:\n /// ```text, no_run\n /// 100\n /// 80\n /// 60\n /// 40\n /// 20\n /// ```\n pub fn parse_performance(response: \u0026str) -\u003e u8 {\n response.parse::\u003cu8\u003e().unwrap()\n }\n\n pub fn parse_num_samples(response: \u0026str) -\u003e u32 {\n response.parse::\u003cu32\u003e().unwrap()\n }\n\n ///\n pub fn parse_get_sample_rate(response: \u0026str) -\u003e SampleRate {\n let mut iter = response.lines();\n SampleRate {\n DigitalSampleRate: iter.next().unwrap().parse::\u003cu32\u003e().unwrap(),\n AnalogSampleRate: iter.next().unwrap().parse::\u003cu32\u003e().unwrap(),\n }\n }\n\n /// Parse get all sample rates\n ///\n /// # Sample input\n /// ```text\n /// 5000000, 1250000\n /// 10000000, 625000\n /// ```\n pub fn parse_get_all_sample_rates(response: \u0026str) -\u003e Vec\u003cSampleRate\u003e {\n response\n .lines()\n .map(|a| SampleRate::from_str(\u0026a).unwrap())\n .collect()\n }\n\n /// Parse the connected_devices reponse into ConnectedDevice\n ///\n /// # Sample Input\n /// ```text\n /// 1, Logic Pro 16, LOGIC_PRO_16_DEVICE, 0xdf03c43d1f3aa2f3, ACTIVE\n /// ```\n pub fn parse_connected_devices(response: \u0026str) -\u003e Vec\u003cConnectedDevice\u003e {\n response\n .lines()\n .map(|a| ConnectedDevice::from_str(\u0026a).unwrap())\n .collect()\n }\n\n pub fn parse_get_active_channels(response: \u0026str) -\u003e Result\u003cVec\u003cVec\u003cu8\u003e\u003e\u003e {\n println!(\"{}\", response);\n let v: Vec\u003c\u0026str\u003e = response.split(',').map(|a| a.trim_start()).collect();\n\n // Find position of starter word\n let digital_pos = v.iter().position(|a| *a == \"digital_channels\").unwrap();\n let analog_pos = v.iter().position(|a| *a == \"analog_channels\").unwrap();\n\n // Parse in between words to find values\n let digital_res: Vec\u003cu8\u003e = v[digital_pos + 1..analog_pos]\n .iter()\n .map(|a| a.parse().unwrap())\n .collect();\n\n let analog_res: Vec\u003cu8\u003e = v[analog_pos + 1..v.len()]\n .iter()\n .map(|a| a.parse().unwrap())\n .collect();\n Ok(vec![digital_res, analog_res])\n }\n\n /// Parse if processing is complete\n ///\n /// # Sample Input\n /// ```text\n /// FALSE\n /// ```\n /// ```text\n /// TRUE\n /// ```\n pub fn parse_processing_complete(response: \u0026str) -\u003e bool {\n response == \"TRUE\"\n }\n}\n","traces":[{"line":14,"address":4279104,"length":1,"stats":{"Line":5}},{"line":15,"address":4279123,"length":1,"stats":{"Line":5}},{"line":22,"address":4279264,"length":1,"stats":{"Line":3}},{"line":23,"address":4279281,"length":1,"stats":{"Line":3}},{"line":37,"address":4279392,"length":1,"stats":{"Line":2}},{"line":38,"address":4279406,"length":1,"stats":{"Line":2}},{"line":41,"address":4279488,"length":1,"stats":{"Line":2}},{"line":42,"address":4279502,"length":1,"stats":{"Line":2}},{"line":46,"address":4279568,"length":1,"stats":{"Line":1}},{"line":47,"address":4279585,"length":1,"stats":{"Line":1}},{"line":49,"address":4279633,"length":1,"stats":{"Line":1}},{"line":50,"address":4279784,"length":1,"stats":{"Line":1}},{"line":61,"address":4279984,"length":1,"stats":{"Line":1}},{"line":62,"address":4280004,"length":1,"stats":{"Line":1}},{"line":74,"address":4280080,"length":1,"stats":{"Line":2}},{"line":75,"address":4280100,"length":1,"stats":{"Line":2}},{"line":81,"address":4280176,"length":1,"stats":{"Line":2}},{"line":82,"address":4280202,"length":1,"stats":{"Line":2}},{"line":83,"address":4280435,"length":1,"stats":{"Line":4}},{"line":86,"address":4280520,"length":1,"stats":{"Line":4}},{"line":87,"address":4280729,"length":1,"stats":{"Line":4}},{"line":90,"address":4280912,"length":1,"stats":{"Line":2}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":92,"address":4336736,"length":1,"stats":{"Line":2}},{"line":93,"address":4281093,"length":1,"stats":{"Line":2}},{"line":95,"address":4281101,"length":1,"stats":{"Line":2}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":97,"address":4336832,"length":1,"stats":{"Line":2}},{"line":98,"address":null,"length":0,"stats":{"Line":0}},{"line":99,"address":4281308,"length":1,"stats":{"Line":2}},{"line":111,"address":4281696,"length":1,"stats":{"Line":2}},{"line":112,"address":4281710,"length":1,"stats":{"Line":2}}],"covered":29,"coverable":32},{"path":["/","home","wcampbell","projects","wcampbell","code","saleae","src","samplerate.rs"],"content":"//! This module defines the sample rates returned and used by the saleae Logic program\n//!\n//! # C# Example\n//! ```text\n//! struct SampleRate\n//! {\n//! public int AnalogSampleRate;\n//! public int DigitalSampleRate;\n//! }\n//! ```\nuse std::str::FromStr;\n\n#[allow(non_snake_case)]\n#[derive(Debug, PartialEq)]\npub struct SampleRate {\n pub DigitalSampleRate: u32,\n pub AnalogSampleRate: u32,\n}\n\nimpl FromStr for SampleRate {\n type Err = std::num::ParseIntError;\n fn from_str(response: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n let v: Vec\u003c\u0026str\u003e = response.split(',').map(|a| a.trim_start()).collect();\n\n Ok(SampleRate {\n DigitalSampleRate: v[0].parse::\u003cu32\u003e().unwrap(),\n AnalogSampleRate: v[1].parse::\u003cu32\u003e().unwrap(),\n })\n }\n}\n","traces":[{"line":22,"address":4211904,"length":1,"stats":{"Line":2}},{"line":23,"address":4211924,"length":1,"stats":{"Line":4}},{"line":25,"address":4212233,"length":1,"stats":{"Line":2}},{"line":26,"address":4212025,"length":1,"stats":{"Line":2}},{"line":27,"address":4212138,"length":1,"stats":{"Line":2}}],"covered":5,"coverable":5},{"path":["/","home","wcampbell","projects","wcampbell","code","saleae","tests","client.rs"],"content":"#[cfg(test)]\nmod tests {\n use saleae::client::{Client, Connection};\n use saleae::device::DeviceID;\n use saleae::ConnectedDevice;\n use saleae::PerformanceOption;\n use saleae::SampleRate;\n\n #[test]\n fn set_num_samples() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn.set_num_samples(500).unwrap();\n assert_eq!(true, response);\n }\n\n #[test]\n fn get_num_samples() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n unsafe { faux::when!(conn.general_recieve_message).then(|_| Ok(\"3000\\nACK\".to_string())) }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn.get_num_samples().unwrap();\n assert_eq!(3000, response);\n }\n\n #[test]\n #[should_panic]\n fn get_num_samples_fail() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n unsafe { faux::when!(conn.general_recieve_message).then(|_| Ok(\"3000\".to_string())) }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn.get_num_samples().unwrap();\n assert_eq!(3000, response);\n }\n\n #[test]\n fn set_capture_seconds() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn.set_capture_seconds(2.2).unwrap();\n assert_eq!(true, response);\n }\n\n #[test]\n fn set_sample_rate() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn\n .set_sample_rate(\u0026SampleRate {\n AnalogSampleRate: 6250000,\n DigitalSampleRate: 1562500,\n })\n .unwrap();\n assert_eq!(true, response);\n }\n\n #[test]\n fn get_sample_rate() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n unsafe {\n faux::when!(conn.general_recieve_message).then(|_| Ok(\"1000000\\n0\\n\".to_string()))\n }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn.get_sample_rate().unwrap();\n assert_eq!(response.DigitalSampleRate, 1000000);\n assert_eq!(response.AnalogSampleRate, 0);\n }\n\n #[test]\n fn get_performance() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n unsafe { faux::when!(conn.general_recieve_message).then(|_| Ok(\"100\".to_string())) }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn.get_performance().unwrap();\n assert_eq!(response, 100);\n }\n\n #[test]\n fn set_performance() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn.set_performance(PerformanceOption::Full).unwrap();\n assert_eq!(response, true);\n\n let mut conn2 = Connection::faux();\n unsafe { faux::when!(conn2.run_command).then(|_| Ok(())) }\n unsafe { faux::when!(conn2.general_recieve_ack).then(|_| Ok(false)) }\n\n let mut conn2 = Client::new(conn2).unwrap();\n let response2 = conn2.set_performance(PerformanceOption::Low).unwrap();\n assert_eq!(response2, false);\n }\n\n #[test]\n fn get_connected_devices() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n unsafe {\n faux::when!(conn.general_recieve_message).then(|_| Ok(\"1, Logic 8, LOGIC_8_DEVICE, 0x2dc9, ACTIVE\\n2, Logic Pro 8, LOGIC_PRO_8_DEVICE, 0x7243\\n3, Logic Pro 16, LOGIC_PRO_16_DEVICE, 0x673f\\n4, Logic 4, LOGIC_4_DEVICE, 0x6709\\nACK\".to_string()))\n }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn.get_connected_devices().unwrap();\n assert_eq!(\n response[0],\n ConnectedDevice {\n d_type: \"1\".to_string(),\n name: \"Logic 8\".to_string(),\n device_id: DeviceID::LOGIC_8_DEVICE,\n index: \"0x2dc9\".to_string(),\n is_active: true\n }\n );\n assert_eq!(\n response[1],\n ConnectedDevice {\n d_type: \"2\".to_string(),\n name: \"Logic Pro 8\".to_string(),\n device_id: DeviceID::LOGIC_PRO_8_DEVICE,\n index: \"0x7243\".to_string(),\n is_active: false\n }\n );\n assert_eq!(\n response[2],\n ConnectedDevice {\n d_type: \"3\".to_string(),\n name: \"Logic Pro 16\".to_string(),\n device_id: DeviceID::LOGIC_PRO_16_DEVICE,\n index: \"0x673f\".to_string(),\n is_active: false\n }\n );\n assert_eq!(\n response[3],\n ConnectedDevice {\n d_type: \"4\".to_string(),\n name: \"Logic 4\".to_string(),\n device_id: DeviceID::LOGIC_4_DEVICE,\n index: \"0x6709\".to_string(),\n is_active: false\n }\n );\n }\n\n #[test]\n fn select_active_device() {\n //TODO can't test b/c of get_connected_devices not being fauxable\n //let device = ConnectedDevice {\n // d_type: \"4\".to_string(),\n // name: \"Logic 4\".to_string(),\n // device_id: DeviceID::LOGIC_4_DEVICE,\n // index: \"0x6709\".to_string(),\n // is_active: false,\n //};\n\n //let mut conn = Connection::faux();\n //unsafe { faux::when!(conn.get_connected_devices).then(|_| Ok(device)) };\n //unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n\n //let mut conn = Client::new(conn).unwrap();\n //let response = conn.select_active_device(device).unwrap();\n //assert_eq!(response, true);\n }\n\n #[test]\n fn get_active_device() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n unsafe {\n faux::when!(conn.general_recieve_message).then(|_| {\n Ok(\"digital_channels, 0, 4, 5, 7, analog_channels, 0, 1, 2, 5, 8\".to_string())\n })\n }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn.get_active_channels().unwrap();\n assert_eq!(response[0], [0, 4, 5, 7]);\n assert_eq!(response[1], [0, 1, 2, 5, 8]);\n }\n\n #[test]\n fn set_active_channels() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn\n .set_active_channels(\u0026[0, 4, 5, 7], \u0026[0, 1, 2, 5, 8])\n .unwrap();\n assert_eq!(response, true);\n }\n\n #[test]\n fn reset_active_channels() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn.reset_active_channels().unwrap();\n assert_eq!(response, true);\n }\n\n #[test]\n fn start_capture() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn.start_capture().unwrap();\n assert_eq!(response, true);\n }\n\n #[test]\n fn start_capture_block_until_finished() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn.start_capture_block_until_finished().unwrap();\n assert_eq!(response, true);\n }\n\n #[test]\n fn is_processing_complete() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n unsafe { faux::when!(conn.general_recieve_message).then(|_| Ok(\"TRUE\\nACK\".to_string())) }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn.is_processing_complete().unwrap();\n assert_eq!(response, true);\n }\n\n #[test]\n fn stop_capture() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn.stop_capture().unwrap();\n assert_eq!(response, true);\n }\n\n #[test]\n fn close_all_tabs() {\n let mut conn = Connection::faux();\n unsafe { faux::when!(conn.run_command).then(|_| Ok(())) }\n unsafe { faux::when!(conn.general_recieve_ack).then(|_| Ok(true)) }\n\n let mut conn = Client::new(conn).unwrap();\n let response = conn.close_all_tabs().unwrap();\n assert_eq!(response, true);\n }\n}\n","traces":[{"line":10,"address":4240032,"length":1,"stats":{"Line":2}},{"line":11,"address":4240039,"length":1,"stats":{"Line":1}},{"line":12,"address":4240083,"length":1,"stats":{"Line":2}},{"line":13,"address":4240122,"length":1,"stats":{"Line":2}},{"line":15,"address":4240161,"length":1,"stats":{"Line":1}},{"line":16,"address":4240294,"length":1,"stats":{"Line":1}},{"line":17,"address":4240383,"length":1,"stats":{"Line":1}},{"line":21,"address":4240912,"length":1,"stats":{"Line":2}},{"line":22,"address":4240919,"length":1,"stats":{"Line":1}},{"line":23,"address":4240963,"length":1,"stats":{"Line":2}},{"line":24,"address":4241002,"length":1,"stats":{"Line":2}},{"line":26,"address":4241041,"length":1,"stats":{"Line":1}},{"line":27,"address":4241174,"length":1,"stats":{"Line":1}},{"line":28,"address":4241256,"length":1,"stats":{"Line":1}},{"line":33,"address":4241776,"length":1,"stats":{"Line":2}},{"line":34,"address":4241783,"length":1,"stats":{"Line":1}},{"line":35,"address":4241827,"length":1,"stats":{"Line":2}},{"line":36,"address":4241866,"length":1,"stats":{"Line":2}},{"line":38,"address":4241905,"length":1,"stats":{"Line":1}},{"line":39,"address":4242038,"length":1,"stats":{"Line":1}},{"line":40,"address":4242120,"length":1,"stats":{"Line":0}},{"line":44,"address":4242640,"length":1,"stats":{"Line":2}},{"line":45,"address":4242647,"length":1,"stats":{"Line":1}},{"line":46,"address":4242691,"length":1,"stats":{"Line":2}},{"line":47,"address":4242730,"length":1,"stats":{"Line":2}},{"line":49,"address":4242769,"length":1,"stats":{"Line":1}},{"line":50,"address":4242902,"length":1,"stats":{"Line":1}},{"line":51,"address":4242994,"length":1,"stats":{"Line":1}},{"line":55,"address":4243520,"length":1,"stats":{"Line":2}},{"line":56,"address":4243527,"length":1,"stats":{"Line":1}},{"line":57,"address":4243571,"length":1,"stats":{"Line":2}},{"line":58,"address":4243610,"length":1,"stats":{"Line":2}},{"line":60,"address":4243649,"length":1,"stats":{"Line":1}},{"line":61,"address":4243789,"length":1,"stats":{"Line":1}},{"line":62,"address":4243782,"length":1,"stats":{"Line":1}},{"line":67,"address":4243873,"length":1,"stats":{"Line":1}},{"line":71,"address":4244400,"length":1,"stats":{"Line":2}},{"line":72,"address":4244407,"length":1,"stats":{"Line":1}},{"line":73,"address":4244451,"length":1,"stats":{"Line":2}},{"line":75,"address":4244490,"length":1,"stats":{"Line":2}},{"line":78,"address":4244529,"length":1,"stats":{"Line":1}},{"line":79,"address":4244671,"length":1,"stats":{"Line":1}},{"line":80,"address":4244768,"length":1,"stats":{"Line":1}},{"line":81,"address":4244865,"length":1,"stats":{"Line":1}},{"line":85,"address":4245728,"length":1,"stats":{"Line":2}},{"line":86,"address":4245735,"length":1,"stats":{"Line":1}},{"line":87,"address":4245779,"length":1,"stats":{"Line":2}},{"line":88,"address":4245818,"length":1,"stats":{"Line":2}},{"line":90,"address":4245857,"length":1,"stats":{"Line":1}},{"line":91,"address":4245990,"length":1,"stats":{"Line":1}},{"line":92,"address":4246072,"length":1,"stats":{"Line":1}},{"line":96,"address":4246608,"length":1,"stats":{"Line":2}},{"line":97,"address":4246615,"length":1,"stats":{"Line":1}},{"line":98,"address":4246670,"length":1,"stats":{"Line":2}},{"line":99,"address":4246712,"length":1,"stats":{"Line":2}},{"line":101,"address":4246754,"length":1,"stats":{"Line":1}},{"line":102,"address":4246899,"length":1,"stats":{"Line":1}},{"line":103,"address":4247005,"length":1,"stats":{"Line":1}},{"line":105,"address":4247100,"length":1,"stats":{"Line":1}},{"line":106,"address":4247437,"length":1,"stats":{"Line":2}},{"line":107,"address":4247487,"length":1,"stats":{"Line":2}},{"line":109,"address":4247529,"length":1,"stats":{"Line":1}},{"line":110,"address":4247674,"length":1,"stats":{"Line":1}},{"line":111,"address":4247774,"length":1,"stats":{"Line":1}},{"line":115,"address":4248400,"length":1,"stats":{"Line":2}},{"line":116,"address":4248407,"length":1,"stats":{"Line":1}},{"line":117,"address":4248454,"length":1,"stats":{"Line":2}},{"line":119,"address":4248496,"length":1,"stats":{"Line":2}},{"line":122,"address":4248538,"length":1,"stats":{"Line":1}},{"line":123,"address":4248683,"length":1,"stats":{"Line":1}},{"line":124,"address":4249034,"length":1,"stats":{"Line":1}},{"line":125,"address":4248774,"length":1,"stats":{"Line":1}},{"line":126,"address":4248908,"length":1,"stats":{"Line":1}},{"line":127,"address":4248789,"length":1,"stats":{"Line":1}},{"line":128,"address":4248831,"length":1,"stats":{"Line":1}},{"line":129,"address":4248858,"length":1,"stats":{"Line":1}},{"line":130,"address":4248866,"length":1,"stats":{"Line":1}},{"line":134,"address":4249805,"length":1,"stats":{"Line":1}},{"line":135,"address":4249557,"length":1,"stats":{"Line":1}},{"line":136,"address":4249679,"length":1,"stats":{"Line":1}},{"line":137,"address":4249572,"length":1,"stats":{"Line":1}},{"line":138,"address":4249599,"length":1,"stats":{"Line":1}},{"line":139,"address":4249626,"length":1,"stats":{"Line":1}},{"line":140,"address":4249634,"length":1,"stats":{"Line":1}},{"line":144,"address":4250576,"length":1,"stats":{"Line":1}},{"line":145,"address":4250328,"length":1,"stats":{"Line":1}},{"line":146,"address":4250450,"length":1,"stats":{"Line":1}},{"line":147,"address":4250343,"length":1,"stats":{"Line":1}},{"line":148,"address":4250370,"length":1,"stats":{"Line":1}},{"line":149,"address":4250397,"length":1,"stats":{"Line":1}},{"line":150,"address":4250405,"length":1,"stats":{"Line":1}},{"line":154,"address":4251305,"length":1,"stats":{"Line":1}},{"line":155,"address":4251063,"length":1,"stats":{"Line":1}},{"line":156,"address":4251182,"length":1,"stats":{"Line":1}},{"line":157,"address":4251075,"length":1,"stats":{"Line":1}},{"line":158,"address":4251102,"length":1,"stats":{"Line":1}},{"line":159,"address":4251129,"length":1,"stats":{"Line":1}},{"line":160,"address":4251137,"length":1,"stats":{"Line":1}},{"line":167,"address":4252160,"length":1,"stats":{"Line":2}},{"line":187,"address":4252176,"length":1,"stats":{"Line":2}},{"line":188,"address":4252183,"length":1,"stats":{"Line":1}},{"line":189,"address":4252230,"length":1,"stats":{"Line":2}},{"line":191,"address":4252272,"length":1,"stats":{"Line":2}},{"line":192,"address":4265934,"length":1,"stats":{"Line":1}},{"line":196,"address":4252314,"length":1,"stats":{"Line":1}},{"line":197,"address":4252459,"length":1,"stats":{"Line":1}},{"line":198,"address":4252546,"length":1,"stats":{"Line":1}},{"line":199,"address":4252698,"length":1,"stats":{"Line":1}},{"line":203,"address":4253600,"length":1,"stats":{"Line":2}},{"line":204,"address":4253607,"length":1,"stats":{"Line":1}},{"line":205,"address":4253651,"length":1,"stats":{"Line":2}},{"line":206,"address":4253690,"length":1,"stats":{"Line":2}},{"line":208,"address":4253729,"length":1,"stats":{"Line":1}},{"line":209,"address":4253876,"length":1,"stats":{"Line":1}},{"line":210,"address":4253862,"length":1,"stats":{"Line":1}},{"line":212,"address":4253971,"length":1,"stats":{"Line":1}},{"line":216,"address":4254496,"length":1,"stats":{"Line":2}},{"line":217,"address":4254503,"length":1,"stats":{"Line":1}},{"line":218,"address":4254547,"length":1,"stats":{"Line":2}},{"line":219,"address":4254586,"length":1,"stats":{"Line":2}},{"line":221,"address":4254625,"length":1,"stats":{"Line":1}},{"line":222,"address":4254758,"length":1,"stats":{"Line":1}},{"line":223,"address":4254842,"length":1,"stats":{"Line":1}},{"line":227,"address":4255360,"length":1,"stats":{"Line":2}},{"line":228,"address":4255367,"length":1,"stats":{"Line":1}},{"line":229,"address":4255411,"length":1,"stats":{"Line":2}},{"line":231,"address":4255450,"length":1,"stats":{"Line":1}},{"line":232,"address":4255583,"length":1,"stats":{"Line":1}},{"line":233,"address":4255667,"length":1,"stats":{"Line":1}},{"line":237,"address":4256192,"length":1,"stats":{"Line":2}},{"line":238,"address":4256199,"length":1,"stats":{"Line":1}},{"line":239,"address":4256243,"length":1,"stats":{"Line":2}},{"line":240,"address":4256282,"length":1,"stats":{"Line":2}},{"line":242,"address":4256321,"length":1,"stats":{"Line":1}},{"line":243,"address":4256454,"length":1,"stats":{"Line":1}},{"line":244,"address":4256538,"length":1,"stats":{"Line":1}},{"line":248,"address":4257056,"length":1,"stats":{"Line":2}},{"line":249,"address":4257063,"length":1,"stats":{"Line":1}},{"line":250,"address":4257107,"length":1,"stats":{"Line":2}},{"line":251,"address":4257146,"length":1,"stats":{"Line":2}},{"line":253,"address":4257185,"length":1,"stats":{"Line":1}},{"line":254,"address":4257318,"length":1,"stats":{"Line":1}},{"line":255,"address":4257402,"length":1,"stats":{"Line":1}},{"line":259,"address":4257920,"length":1,"stats":{"Line":2}},{"line":260,"address":4257927,"length":1,"stats":{"Line":1}},{"line":261,"address":4257971,"length":1,"stats":{"Line":2}},{"line":262,"address":4258010,"length":1,"stats":{"Line":2}},{"line":264,"address":4258049,"length":1,"stats":{"Line":1}},{"line":265,"address":4258182,"length":1,"stats":{"Line":1}},{"line":266,"address":4258266,"length":1,"stats":{"Line":1}},{"line":270,"address":4258784,"length":1,"stats":{"Line":2}},{"line":271,"address":4258791,"length":1,"stats":{"Line":1}},{"line":272,"address":4258835,"length":1,"stats":{"Line":2}},{"line":273,"address":4258874,"length":1,"stats":{"Line":2}},{"line":275,"address":4258913,"length":1,"stats":{"Line":1}},{"line":276,"address":4259046,"length":1,"stats":{"Line":1}},{"line":277,"address":4259130,"length":1,"stats":{"Line":1}}],"covered":156,"coverable":157},{"path":["/","home","wcampbell","projects","wcampbell","code","saleae","tests","device.rs"],"content":"#[cfg(test)]\nmod tests {\n use saleae::device::{ConnectedDevice, DeviceID};\n use std::str::FromStr;\n\n // Sample from C# API\n //1, Logic 8, LOGIC_8_DEVICE, 0x2dc9, ACTIVE\n //2, Logic Pro 8, LOGIC_PRO_8_DEVICE, 0x7243\n //3, Logic Pro 16, LOGIC_PRO_16_DEVICE, 0x673f\n //4, Logic 4, LOGIC_4_DEVICE, 0x6709\n #[test]\n fn test_from_str() {\n // test whether from_str performs as expected\n assert_eq!(\n ConnectedDevice::from_str(\n \u0026\"1, Logic Pro 16, LOGIC_PRO_16_DEVICE, 0xdf03c43d1f3aa2f3, ACTIVE\"\n )\n .unwrap(),\n ConnectedDevice {\n d_type: \"1\".to_string(),\n name: \"Logic Pro 16\".to_string(),\n device_id: DeviceID::LOGIC_PRO_16_DEVICE,\n index: \"0xdf03c43d1f3aa2f3\".to_string(),\n is_active: true,\n }\n );\n assert_eq!(\n ConnectedDevice::from_str(\u0026\"2, Logic 8, LOGIC_8_DEVICE, 0xffffffffff, ACTIVE\").unwrap(),\n ConnectedDevice {\n d_type: \"2\".to_string(),\n name: \"Logic 8\".to_string(),\n device_id: DeviceID::LOGIC_8_DEVICE,\n index: \"0xffffffffff\".to_string(),\n is_active: true,\n }\n );\n assert_eq!(\n ConnectedDevice::from_str(\u0026\"1, Logic Pro 16, LOGIC_PRO_16_DEVICE, 0xdf03c43d1f3aa2f3\")\n .unwrap(),\n ConnectedDevice {\n d_type: \"1\".to_string(),\n name: \"Logic Pro 16\".to_string(),\n device_id: DeviceID::LOGIC_PRO_16_DEVICE,\n index: \"0xdf03c43d1f3aa2f3\".to_string(),\n is_active: false,\n }\n );\n }\n}\n","traces":[{"line":12,"address":4214576,"length":1,"stats":{"Line":2}},{"line":14,"address":4214687,"length":1,"stats":{"Line":0}},{"line":15,"address":4214597,"length":1,"stats":{"Line":1}},{"line":16,"address":4214583,"length":1,"stats":{"Line":1}},{"line":19,"address":4214779,"length":1,"stats":{"Line":1}},{"line":20,"address":4214660,"length":1,"stats":{"Line":1}},{"line":21,"address":4214702,"length":1,"stats":{"Line":1}},{"line":22,"address":4214729,"length":1,"stats":{"Line":1}},{"line":23,"address":4214737,"length":1,"stats":{"Line":1}},{"line":27,"address":4215515,"length":1,"stats":{"Line":0}},{"line":28,"address":4215428,"length":1,"stats":{"Line":1}},{"line":29,"address":4215610,"length":1,"stats":{"Line":1}},{"line":30,"address":4215488,"length":1,"stats":{"Line":1}},{"line":31,"address":4215533,"length":1,"stats":{"Line":1}},{"line":32,"address":4215560,"length":1,"stats":{"Line":1}},{"line":33,"address":4215568,"length":1,"stats":{"Line":1}},{"line":37,"address":4216304,"length":1,"stats":{"Line":0}},{"line":38,"address":4216217,"length":1,"stats":{"Line":1}},{"line":40,"address":4216399,"length":1,"stats":{"Line":1}},{"line":41,"address":4216277,"length":1,"stats":{"Line":1}},{"line":42,"address":4216322,"length":1,"stats":{"Line":1}},{"line":43,"address":4216349,"length":1,"stats":{"Line":1}},{"line":44,"address":4216357,"length":1,"stats":{"Line":1}}],"covered":20,"coverable":23},{"path":["/","home","wcampbell","projects","wcampbell","code","saleae","tests","request.rs"],"content":"#[cfg(test)]\nmod tests {\n use saleae::request::Request;\n /// Regular function tests\n #[test]\n fn test_prepare_set_active_channels() {\n let expected1: String =\n \"set_active_channels, digital_channels, 1, 7, 8, 9, analog_channels, 0, 9, 8\\0\"\n .to_string();\n assert_eq!(\n expected1,\n Request::prepare_set_active_channels(\u0026[1, 7, 8, 9], \u0026[0, 9, 8]).unwrap(),\n );\n\n // Empty digital channel\n let expected2: String = \"set_active_channels, analog_channels, 0, 9, 8\\0\".to_string();\n assert_eq!(\n expected2,\n Request::prepare_set_active_channels(\u0026[], \u0026[0, 9, 8]).unwrap(),\n );\n\n // Empty analog channel\n let expected3: String = \"set_active_channels, digital_channels, 1, 7, 8, 9\\0\".to_string();\n assert_eq!(\n expected3,\n Request::prepare_set_active_channels(\u0026[1, 7, 8, 9], \u0026[]).unwrap(),\n );\n }\n\n #[test]\n #[should_panic(\n expected = \"Logic requires at least one active channel, no active channels found\"\n )]\n fn test_prepare_set_active_channels_panic() {\n // Empty both channels (should panic / error)\n let expected4: String = \"set_active_channels\\0\".to_string();\n assert_eq!(\n expected4,\n Request::prepare_set_active_channels(\u0026[], \u0026[]).unwrap(),\n );\n }\n\n /// Help function tests\n #[test]\n fn test_create_channel_str() {\n let input = [0, 4, 5, 7];\n assert_eq!(\"0, 4, 5, 7\", Request::create_channel_str(\u0026input).unwrap());\n\n let input = [1, 2, 3, 5, 8];\n assert_eq!(\n \"1, 2, 3, 5, 8\",\n Request::create_channel_str(\u0026input).unwrap()\n );\n }\n}\n","traces":[{"line":6,"address":4211296,"length":1,"stats":{"Line":2}},{"line":8,"address":4211310,"length":1,"stats":{"Line":1}},{"line":10,"address":4211445,"length":1,"stats":{"Line":1}},{"line":12,"address":4211348,"length":1,"stats":{"Line":1}},{"line":16,"address":4211934,"length":1,"stats":{"Line":1}},{"line":17,"address":4212058,"length":1,"stats":{"Line":1}},{"line":19,"address":4211961,"length":1,"stats":{"Line":1}},{"line":23,"address":4212505,"length":1,"stats":{"Line":1}},{"line":24,"address":4212631,"length":1,"stats":{"Line":1}},{"line":26,"address":4212532,"length":1,"stats":{"Line":1}},{"line":34,"address":4213248,"length":1,"stats":{"Line":2}},{"line":36,"address":4213262,"length":1,"stats":{"Line":1}},{"line":37,"address":4213405,"length":1,"stats":{"Line":0}},{"line":39,"address":4213297,"length":1,"stats":{"Line":1}},{"line":45,"address":4213904,"length":1,"stats":{"Line":2}},{"line":46,"address":4213911,"length":1,"stats":{"Line":1}},{"line":47,"address":4213943,"length":1,"stats":{"Line":1}},{"line":49,"address":4214500,"length":1,"stats":{"Line":1}},{"line":50,"address":4214540,"length":1,"stats":{"Line":1}},{"line":52,"address":4214547,"length":1,"stats":{"Line":1}}],"covered":19,"coverable":20},{"path":["/","home","wcampbell","projects","wcampbell","code","saleae","tests","response.rs"],"content":"#[cfg(test)]\nmod tests {\n use saleae::device::{ConnectedDevice, DeviceID};\n use saleae::response::Response;\n use saleae::samplerate::SampleRate;\n\n #[test]\n fn test_remove_ack() {\n let test0 = String::from(\"let\\nmy\\npeople\\ngo\\nACK\");\n assert_eq!(\"let\\nmy\\npeople\\ngo\", Response::remove_ack(\u0026test0));\n\n let test1 = String::from(\"\\nACK\");\n assert_eq!(\"\", Response::remove_ack(\u0026test1));\n\n let test2 = String::from(\"let\\nmy\\npeople\\ngo\\nACK\\u{0}\\u{0}\");\n assert_eq!(\"let\\nmy\\npeople\\ngo\", Response::remove_ack(\u0026test2));\n }\n\n #[test]\n fn test_verify_ack() {\n let test0 = String::from(\"let\\nmy\\npeople\\ngo\\nACK\");\n assert!(Response::verify_ack(\u0026test0));\n\n let test1 = String::from(\"ACK\");\n assert!(Response::verify_ack(\u0026test1));\n\n let test2 = String::from(\"ack\");\n assert_eq!(false, Response::verify_ack(\u0026test2));\n\n let test3 = String::from(\"let\\nmy\\npeople\\ngo\\n\");\n assert_eq!(false, Response::verify_ack(\u0026test3));\n }\n\n #[test]\n fn test_parse_performance() {\n assert_eq!(Response::parse_performance(\"100\"), 100);\n assert_eq!(Response::parse_performance(\"80\"), 80);\n assert_eq!(Response::parse_performance(\"60\"), 60);\n assert_eq!(Response::parse_performance(\"40\"), 40);\n assert_eq!(Response::parse_performance(\"20\"), 20);\n }\n\n #[test]\n fn test_get_all_sample_rates() {\n let one = SampleRate {\n DigitalSampleRate: 25000000,\n AnalogSampleRate: 3125000,\n };\n let two = SampleRate {\n DigitalSampleRate: 6250000,\n AnalogSampleRate: 1562500,\n };\n let result = Response::parse_get_all_sample_rates(\"25000000, 3125000\\n6250000, 1562500\\n\");\n assert_eq!(result[0], one);\n assert_eq!(result[1], two);\n }\n\n #[test]\n fn test_connected_devices() {\n let one = ConnectedDevice {\n d_type: \"2\".to_string(),\n name: \"Logic 8\".to_string(),\n device_id: DeviceID::LOGIC_8_DEVICE,\n index: \"0xffffffffff\".to_string(),\n is_active: true,\n };\n let two = ConnectedDevice {\n d_type: \"1\".to_string(),\n name: \"Logic Pro 16\".to_string(),\n device_id: DeviceID::LOGIC_PRO_16_DEVICE,\n index: \"0xdf03c43d1f3aa2f3\".to_string(),\n is_active: false,\n };\n let expected = vec![one, two];\n let result = Response::parse_connected_devices(\n \"2, Logic 8, LOGIC_8_DEVICE, 0xffffffffff, ACTIVE\n 1, Logic Pro 16, LOGIC_PRO_16_DEVICE, 0xdf03c43d1f3aa2f3\",\n );\n assert_eq!(result[0], expected[0]);\n assert_eq!(result[1], expected[1]);\n }\n\n #[test]\n fn test_parse_into_active_channels() {\n // Test regular input\n let input: String = \"digital_channels, 0, 4, 5, 7, analog_channels, 0, 1, 2, 5\".to_string();\n let result = Response::parse_get_active_channels(\u0026input).unwrap();\n assert_eq!([0, 4, 5, 7], result[0].as_slice());\n assert_eq!([0, 1, 2, 5], result[1].as_slice());\n\n // Test with no analog channels\n let input: String = \"digital_channels, 0, 4, 5, 7, analog_channels\".to_string();\n let result = Response::parse_get_active_channels(\u0026input).unwrap();\n assert_eq!([0, 4, 5, 7], result[0].as_slice());\n assert!(result[1].as_slice().is_empty());\n\n // Test with no digital channels\n let input: String = \"digital_channels, analog_channels, 1, 2, 5\".to_string();\n let result = Response::parse_get_active_channels(\u0026input).unwrap();\n assert!(result[0].as_slice().is_empty());\n assert_eq!([1, 2, 5], result[1].as_slice());\n }\n\n #[test]\n fn test_is_processing_complete() {\n assert_eq!(true, Response::parse_processing_complete(\"TRUE\"));\n assert_eq!(false, Response::parse_processing_complete(\"FALSE\"));\n }\n\n #[test]\n fn test_parse_num_samples() {\n assert_eq!(10000, Response::parse_num_samples(\"10000\"));\n assert_eq!(20, Response::parse_num_samples(\"20\"));\n }\n}\n","traces":[{"line":8,"address":4212736,"length":1,"stats":{"Line":2}},{"line":9,"address":4212750,"length":1,"stats":{"Line":1}},{"line":10,"address":4212788,"length":1,"stats":{"Line":1}},{"line":12,"address":4213386,"length":1,"stats":{"Line":1}},{"line":13,"address":4213413,"length":1,"stats":{"Line":1}},{"line":15,"address":4213984,"length":1,"stats":{"Line":1}},{"line":16,"address":4214011,"length":1,"stats":{"Line":1}},{"line":20,"address":4214720,"length":1,"stats":{"Line":2}},{"line":21,"address":4214734,"length":1,"stats":{"Line":1}},{"line":22,"address":4214780,"length":1,"stats":{"Line":1}},{"line":24,"address":4214865,"length":1,"stats":{"Line":1}},{"line":25,"address":4214933,"length":1,"stats":{"Line":1}},{"line":27,"address":4215021,"length":1,"stats":{"Line":1}},{"line":28,"address":4215081,"length":1,"stats":{"Line":1}},{"line":30,"address":4215291,"length":1,"stats":{"Line":1}},{"line":31,"address":4215654,"length":1,"stats":{"Line":1}},{"line":35,"address":4216304,"length":1,"stats":{"Line":2}},{"line":36,"address":4216318,"length":1,"stats":{"Line":1}},{"line":37,"address":4216440,"length":1,"stats":{"Line":1}},{"line":38,"address":4216901,"length":1,"stats":{"Line":1}},{"line":39,"address":4217362,"length":1,"stats":{"Line":1}},{"line":40,"address":4217799,"length":1,"stats":{"Line":1}},{"line":44,"address":4218528,"length":1,"stats":{"Line":2}},{"line":45,"address":4218542,"length":1,"stats":{"Line":1}},{"line":49,"address":4218564,"length":1,"stats":{"Line":1}},{"line":53,"address":4218586,"length":1,"stats":{"Line":1}},{"line":54,"address":4218637,"length":1,"stats":{"Line":1}},{"line":55,"address":4218790,"length":1,"stats":{"Line":1}},{"line":59,"address":4219584,"length":1,"stats":{"Line":2}},{"line":60,"address":4219598,"length":1,"stats":{"Line":1}},{"line":61,"address":4219606,"length":1,"stats":{"Line":1}},{"line":62,"address":4219644,"length":1,"stats":{"Line":1}},{"line":63,"address":4219671,"length":1,"stats":{"Line":1}},{"line":64,"address":4219679,"length":1,"stats":{"Line":1}},{"line":67,"address":4219988,"length":1,"stats":{"Line":1}},{"line":68,"address":4219847,"length":1,"stats":{"Line":1}},{"line":69,"address":4219892,"length":1,"stats":{"Line":1}},{"line":70,"address":4219938,"length":1,"stats":{"Line":1}},{"line":71,"address":4219946,"length":1,"stats":{"Line":1}},{"line":74,"address":4220116,"length":1,"stats":{"Line":1}},{"line":75,"address":4220455,"length":1,"stats":{"Line":1}},{"line":79,"address":4220498,"length":1,"stats":{"Line":1}},{"line":80,"address":4220693,"length":1,"stats":{"Line":1}},{"line":84,"address":4221680,"length":1,"stats":{"Line":2}},{"line":86,"address":4221694,"length":1,"stats":{"Line":1}},{"line":87,"address":4221740,"length":1,"stats":{"Line":1}},{"line":88,"address":4221843,"length":1,"stats":{"Line":1}},{"line":89,"address":4222073,"length":1,"stats":{"Line":1}},{"line":92,"address":4222646,"length":1,"stats":{"Line":1}},{"line":93,"address":4223041,"length":1,"stats":{"Line":1}},{"line":94,"address":4223147,"length":1,"stats":{"Line":1}},{"line":95,"address":4223390,"length":1,"stats":{"Line":1}},{"line":98,"address":4223843,"length":1,"stats":{"Line":1}},{"line":99,"address":4223911,"length":1,"stats":{"Line":1}},{"line":100,"address":4224029,"length":1,"stats":{"Line":1}},{"line":101,"address":4224119,"length":1,"stats":{"Line":1}},{"line":105,"address":4224880,"length":1,"stats":{"Line":2}},{"line":106,"address":4224894,"length":1,"stats":{"Line":1}},{"line":107,"address":4225026,"length":1,"stats":{"Line":1}},{"line":111,"address":4225776,"length":1,"stats":{"Line":2}},{"line":112,"address":4225790,"length":1,"stats":{"Line":1}},{"line":113,"address":4225919,"length":1,"stats":{"Line":1}}],"covered":62,"coverable":62},{"path":["/","home","wcampbell","projects","wcampbell","code","saleae","tests","samplerate.rs"],"content":"#[cfg(test)]\nmod tests {\n use saleae::samplerate::SampleRate;\n use std::str::FromStr;\n\n #[test]\n fn test_from_str() {\n assert_eq!(\n SampleRate::from_str(\u0026\"5000000, 1250000\").unwrap(),\n SampleRate {\n DigitalSampleRate: 5000000,\n AnalogSampleRate: 1250000\n }\n );\n assert_eq!(\n SampleRate::from_str(\u0026\"10000000, 625000\").unwrap(),\n SampleRate {\n DigitalSampleRate: 10000000,\n AnalogSampleRate: 625000\n }\n );\n }\n}\n","traces":[{"line":7,"address":4212176,"length":1,"stats":{"Line":2}},{"line":8,"address":4213225,"length":1,"stats":{"Line":1}},{"line":9,"address":4213159,"length":1,"stats":{"Line":1}},{"line":15,"address":4213684,"length":1,"stats":{"Line":1}},{"line":16,"address":4213317,"length":1,"stats":{"Line":1}}],"covered":5,"coverable":5}]};</script>
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script>const e = React.createElement;
function pathToString(path) {
if (path[0] === '/') {
return '/' + path.slice(1).join('/');
} else {
return path.join('/');
}
}
function findCommonPath(files) {
if (!files || !files.length) {
return [];
}
function isPrefix(arr, prefix) {
if (arr.length < prefix.length) {
return false;
}
for (let i = prefix.length - 1; i >= 0; --i) {
if (arr[i] !== prefix[i]) {
return false;
}
}
return true;
}
let commonPath = files[0].path.slice(0, -1);
while (commonPath.length) {
if (files.every(file => isPrefix(file.path, commonPath))) {
break;
}
commonPath.pop();
}
return commonPath;
}
function findFolders(files) {
if (!files || !files.length) {
return [];
}
let folders = files.filter(file => file.path.length > 1).map(file => file.path[0]);
folders = [...new Set(folders)]; // unique
folders.sort();
folders = folders.map(folder => {
let filesInFolder = files
.filter(file => file.path[0] === folder)
.map(file => ({
...file,
path: file.path.slice(1),
parent: [...file.parent, file.path[0]],
}));
const children = findFolders(filesInFolder); // recursion
return {
is_folder: true,
path: [folder],
parent: files[0].parent,
children,
covered: children.reduce((sum, file) => sum + file.covered, 0),
coverable: children.reduce((sum, file) => sum + file.coverable, 0),
};
});
return [
...folders,
...files.filter(file => file.path.length === 1),
];
}
class App extends React.Component {
constructor(...args) {
super(...args);
this.state = {
current: [],
};
}
componentDidMount() {
this.updateStateFromLocation();
window.addEventListener("hashchange", () => this.updateStateFromLocation(), false);
}
updateStateFromLocation() {
if (window.location.hash.length > 1) {
const current = window.location.hash.substr(1).split('/');
this.setState({current});
} else {
this.setState({current: []});
}
}
getCurrentPath() {
let file = this.props.root;
let path = [file];
for (let p of this.state.current) {
file = file.children.find(file => file.path[0] === p);
if (!file) {
return path;
}
path.push(file);
}
return path;
}
render() {
const path = this.getCurrentPath();
const file = path[path.length - 1];
let w = null;
if (file.is_folder) {
w = e(FilesList, {
folder: file,
onSelectFile: this.selectFile.bind(this),
onBack: path.length > 1 ? this.back.bind(this) : null,
});
} else {
w = e(DisplayFile, {
file,
onBack: this.back.bind(this),
});
}
return e('div', {className: 'app'}, w);
}
selectFile(file) {
this.setState(({current}) => {
return {current: [...current, file.path[0]]};
}, () => this.updateHash());
}
back(file) {
this.setState(({current}) => {
return {current: current.slice(0, current.length - 1)};
}, () => this.updateHash());
}
updateHash() {
if (!this.state.current || !this.state.current.length) {
window.location = '#';
} else {
window.location = '#' + this.state.current.join('/');
}
}
}
function FilesList({folder, onSelectFile, onBack}) {
let files = folder.children;
return e('div', {className: 'display-folder'},
e(FileHeader, {file: folder, onBack}),
e('table', {className: 'files-list'},
e('thead', {className: 'files-list__head'},
e('tr', null,
e('th', null, "Path"),
e('th', null, "Coverage")
)
),
e('tbody', {className: 'files-list__body'},
files.map(file => e(File, {file, onClick: onSelectFile}))
)
)
);
}
function File({file, onClick}) {
const coverage = file.coverable ? file.covered / file.coverable * 100 : -1;
return e('tr', {
className: 'files-list__file'
+ (coverage >= 0 && coverage < 50 ? ' files-list__file_low': '')
+ (coverage >= 50 && coverage < 80 ? ' files-list__file_medium': '')
+ (coverage >= 80 ? ' files-list__file_high': '')
+ (file.is_folder ? ' files-list__file_folder': ''),
onClick: () => onClick(file),
},
e('td', null, pathToString(file.path)),
e('td', null,
file.covered + ' / ' + file.coverable +
(coverage >= 0 ? ' (' + coverage.toFixed(2) + '%)' : '')
)
);
}
function DisplayFile({file, onBack}) {
return e('div', {className: 'display-file'},
e(FileHeader, {file, onBack}),
e(FileContent, {file})
);
}
function FileHeader({file, onBack}) {
return e('div', {className: 'file-header'},
onBack ? e('a', {className: 'file-header__back', onClick: onBack}, 'Back') : null,
e('div', {className: 'file-header__name'}, pathToString([...file.parent, ...file.path])),
e('div', {className: 'file-header__stat'},
'Covered: ' + file.covered + ' of ' + file.coverable +
(file.coverable ? ' (' + (file.covered / file.coverable * 100).toFixed(2) + '%)' : '')
)
);
}
function FileContent({file}) {
return e('div', {className: 'file-content'},
file.content.split(/\r?\n/).map((line, index) => {
const trace = file.traces.find(trace => trace.line === index + 1);
const covered = trace && trace.stats.Line;
const uncovered = trace && !trace.stats.Line;
return e('pre', {
className: 'code-line'
+ (covered ? ' code-line_covered' : '')
+ (uncovered ? ' code-line_uncovered' : ''),
title: trace ? JSON.stringify(trace.stats, null, 2) : null,
}, line);
})
);
}
(function(){
const commonPath = findCommonPath(data.files);
const files = data.files.map(file => ({...file, path: file.path.slice(commonPath.length), parent: commonPath}));
const children = findFolders(files);
const root = {
is_folder: true,
children,
path: commonPath,
parent: [],
covered: children.reduce((sum, file) => sum + file.covered, 0),
coverable: children.reduce((sum, file) => sum + file.coverable, 0),
};
ReactDOM.render(e(App, {root}), document.getElementById('root'));
}());
</script>
</body>
</html>