Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ humantime = "^2.2.0"
bytesize = "^2.0.1"
hashbrown = "^0.16.0"
ouroboros = "^0.18.5"
thiserror = "^2.0.11"

[package]
name = "ixa"
Expand Down Expand Up @@ -110,6 +111,7 @@ bytesize.workspace = true
hashbrown.workspace = true
xxhash-rust.workspace = true
rustc-hash.workspace = true
thiserror.workspace = true

# Building docs
clap-markdown = { workspace = true, optional = true }
Expand Down
1 change: 1 addition & 0 deletions ixa-bench/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ anyhow.workspace = true
ctor.workspace = true
nohash-hasher = "0.2.0"
tempfile.workspace = true
thiserror.workspace = true

[lib]
bench = false
Expand Down
52 changes: 42 additions & 10 deletions ixa-bench/src/check_criterion_regressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::{env, fs};

use ixa::{HashSet, HashSetExt};
use serde_json::Value;
use thiserror::Error;

struct Est {
group: String,
Expand All @@ -15,6 +16,32 @@ struct Est {

type TableRow = (String, String, String, String, String);

#[derive(Error, Debug)]
enum ReadEstError {
#[error("read error {path}: {source}")]
ReadFile {
path: String,
#[source]
source: std::io::Error,
},
#[error("json parse {path}: {source}")]
JsonParse {
path: String,
#[source]
source: serde_json::Error,
},
#[error("missing mean")]
MissingMean,
#[error("missing point_estimate")]
MissingPointEstimate,
#[error("missing confidence_interval")]
MissingConfidenceInterval,
#[error("missing lower_bound")]
MissingLowerBound,
#[error("missing upper_bound")]
MissingUpperBound,
}

fn find_change_files(base: &Path) -> Vec<(String, String, std::path::PathBuf)> {
let mut results = Vec::new();
if !base.exists() {
Expand Down Expand Up @@ -61,27 +88,32 @@ fn find_change_files(base: &Path) -> Vec<(String, String, std::path::PathBuf)> {
results
}

fn read_est(path: &Path) -> Result<(f64, f64, f64), String> {
let data =
fs::read_to_string(path).map_err(|e| format!("read error {}: {}", path.display(), e))?;
let v: Value =
serde_json::from_str(&data).map_err(|e| format!("json parse {}: {}", path.display(), e))?;
let mean = v.get("mean").ok_or_else(|| "missing mean".to_string())?;
fn read_est(path: &Path) -> Result<(f64, f64, f64), ReadEstError> {
let path_str = path.display().to_string();
let data = fs::read_to_string(path).map_err(|source| ReadEstError::ReadFile {
path: path_str.clone(),
source,
})?;
let v: Value = serde_json::from_str(&data).map_err(|source| ReadEstError::JsonParse {
path: path_str.clone(),
source,
})?;
let mean = v.get("mean").ok_or(ReadEstError::MissingMean)?;
let pe = mean
.get("point_estimate")
.and_then(|x| x.as_f64())
.ok_or_else(|| "missing point_estimate".to_string())?;
.ok_or(ReadEstError::MissingPointEstimate)?;
let ci = mean
.get("confidence_interval")
.ok_or_else(|| "missing confidence_interval".to_string())?;
.ok_or(ReadEstError::MissingConfidenceInterval)?;
let lb = ci
.get("lower_bound")
.and_then(|x| x.as_f64())
.ok_or_else(|| "missing lower_bound".to_string())?;
.ok_or(ReadEstError::MissingLowerBound)?;
let ub = ci
.get("upper_bound")
.and_then(|x| x.as_f64())
.ok_or_else(|| "missing upper_bound".to_string())?;
.ok_or(ReadEstError::MissingUpperBound)?;
Ok((pe, lb, ub))
}

Expand Down
45 changes: 28 additions & 17 deletions src/debugger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,28 @@ use std::fmt::Write;

use clap::{ArgMatches, Command, FromArgMatches, Parser, Subcommand};
use rustyline;
use thiserror::Error;

use crate::external_api::{breakpoint, global_properties, halt, next, run_ext_api};
use crate::{define_data_plugin, trace, Context, HashMap, HashMapExt, IxaError};
use crate::{define_data_plugin, trace, Context, HashMap, HashMapExt};

#[derive(Error, Debug)]
enum DebuggerError {
#[error("error splitting line")]
SplitLine,
#[error(transparent)]
Clap(#[from] clap::Error),
#[error("unknown command: {command}")]
UnknownCommand { command: String },
}

trait DebuggerCommand {
/// Handle the command and any inputs; returning true will exit the debugger
fn handle(
&self,
context: &mut Context,
matches: &ArgMatches,
) -> Result<(bool, Option<String>), String>;
) -> Result<(bool, Option<String>), DebuggerError>;
fn extend(&self, command: Command) -> Command;
}

Expand Down Expand Up @@ -59,13 +70,12 @@ impl Debugger {
&self,
l: &str,
context: &mut Context,
) -> Result<(bool, Option<String>), String> {
let args = shlex::split(l).ok_or("Error splitting lines")?;
) -> Result<(bool, Option<String>), DebuggerError> {
let args = shlex::split(l).ok_or(DebuggerError::SplitLine)?;
let matches = self
.cli
.clone() // cli can only be used once.
.try_get_matches_from(args)
.map_err(|e| e.to_string())?;
.try_get_matches_from(args)?;

if let Some((command, _)) = matches.subcommand() {
// If the provided command is known, run its handler
Expand All @@ -74,7 +84,9 @@ impl Debugger {
return handler.handle(context, &matches);
}
// Unexpected command: print an error
return Err(format!("error: Unknown command: {command}"));
return Err(DebuggerError::UnknownCommand {
command: command.to_string(),
});
}

unreachable!("subcommand required");
Expand All @@ -87,11 +99,10 @@ impl DebuggerCommand for GlobalPropertyCommand {
&self,
context: &mut Context,
matches: &ArgMatches,
) -> Result<(bool, Option<String>), String> {
) -> Result<(bool, Option<String>), DebuggerError> {
let args = global_properties::Args::from_arg_matches(matches).unwrap();
let ret = run_ext_api::<global_properties::Api>(context, &args);
match ret {
Err(IxaError::IxaError(e)) => Ok((false, Some(format!("error: {e}")))),
Err(e) => Ok((false, Some(format!("error: {e}")))),
Ok(global_properties::Retval::List(properties)) => Ok((
false,
Expand All @@ -116,7 +127,7 @@ impl DebuggerCommand for HaltCommand {
&self,
context: &mut Context,
_matches: &ArgMatches,
) -> Result<(bool, Option<String>), String> {
) -> Result<(bool, Option<String>), DebuggerError> {
context.shutdown();
Ok((true, None))
}
Expand All @@ -132,7 +143,7 @@ impl DebuggerCommand for NextCommand {
&self,
context: &mut Context,
_matches: &ArgMatches,
) -> Result<(bool, Option<String>), String> {
) -> Result<(bool, Option<String>), DebuggerError> {
// We execute directly instead of setting `Context::break_requested` so as not to interfere
// with anything else that might be requesting a break, or in case debugger sessions become
// stateful.
Expand All @@ -151,10 +162,10 @@ impl DebuggerCommand for BreakpointCommand {
&self,
context: &mut Context,
matches: &ArgMatches,
) -> Result<(bool, Option<String>), String> {
) -> Result<(bool, Option<String>), DebuggerError> {
let args = breakpoint::Args::from_arg_matches(matches).unwrap();
match run_ext_api::<breakpoint::Api>(context, &args) {
Err(IxaError::IxaError(e)) => Ok((false, Some(format!("error: {e}")))),
Err(e) => Ok((false, Some(format!("error: {e}")))),
Ok(return_value) => {
match return_value {
breakpoint::Retval::List(bp_list) => {
Expand All @@ -169,7 +180,6 @@ impl DebuggerCommand for BreakpointCommand {

Ok((false, None))
}
_ => unimplemented!(),
}
}
fn extend(&self, command: Command) -> Command {
Expand All @@ -188,7 +198,7 @@ impl DebuggerCommand for ContinueCommand {
&self,
_context: &mut Context,
_matches: &ArgMatches,
) -> Result<(bool, Option<String>), String> {
) -> Result<(bool, Option<String>), DebuggerError> {
Ok((true, None))
}
fn extend(&self, command: Command) -> Command {
Expand Down Expand Up @@ -384,14 +394,15 @@ mod tests {
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("required arguments were not provided"));
}

#[test]
fn test_cli_debugger_global_get_unregistered_prop() {
let context = &mut Context::new();
let (_quits, output) = process_line("global get NotExist\n", context);
assert_eq!(output.unwrap(), "error: No global property: NotExist");
assert_eq!(output.unwrap(), "error: no global property: NotExist");
}

#[test]
Expand All @@ -411,7 +422,7 @@ mod tests {
let (_quits, output) = process_line("global get ixa.EmptyGlobal\n", context);
assert_eq!(
output.unwrap(),
"error: Property ixa.EmptyGlobal is not set"
"error: property ixa.EmptyGlobal is not set"
);
}

Expand Down
4 changes: 2 additions & 2 deletions src/entity/context_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ impl ContextEntitiesExt for Context {

// Check that all required properties are present.
if !PL::contains_required_properties() {
return Err("initialization list is missing required properties".into());
return Err(IxaError::MissingRequiredInitializationProperties);
}

// Now that we know we will succeed, we create the entity.
Expand Down Expand Up @@ -552,7 +552,7 @@ mod tests {

assert!(matches!(
result,
Err(crate::IxaError::IxaError(ref msg)) if msg == "initialization list is missing required properties"
Err(crate::IxaError::MissingRequiredInitializationProperties)
));
}

Expand Down
9 changes: 4 additions & 5 deletions src/entity/property_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,10 @@ macro_rules! impl_property_list {
for i in 0..$ct - 1 {
for j in (i + 1)..$ct {
if property_type_ids[i] == property_type_ids[j] {
return Err(format!(
"the same property appears in both position {} and {} in the property list",
i,
j
).into());
return Err(IxaError::DuplicatePropertyInPropertyList {
first_index: i,
second_index: j,
});
}
}
}
Expand Down
Loading