Skip to content

Commit

Permalink
pulling fastsim-2 updates to get tests to pass
Browse files Browse the repository at this point in the history
  • Loading branch information
robinsteuteville committed Jan 10, 2024
2 parents 1301482 + 5910d9d commit f6c607f
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 153 deletions.
77 changes: 77 additions & 0 deletions .github/workflows/build-cli.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: build-cli

on:
release:
types: [published]
workflow_dispatch:

jobs:
build:
name: build command line interfaces for ${{ matrix.platform || matrix.os }}
strategy:
fail-fast: true
matrix:
os:
- ubuntu
- macos
- windows
include:
- os: ubuntu
platform: linux
- os: windows
ls: dir

runs-on: ${{ format('{0}-latest', matrix.os) }}

steps:
- uses: actions/checkout@v3

- name: set up rust
if: matrix.os != 'ubuntu'
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true

- name: set up rust for ubuntu
if: matrix.os == 'ubuntu'
run: >
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain=nightly --profile=minimal -y &&
rustup show
- run: rustup target add aarch64-apple-darwin
if: matrix.os == 'macos'

- name: run cargo tests
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true

- run: cd rust/fastsim-cli/ && cargo test

- run: cd rust/fastsim-cli/ && cargo build --release

- name: list current directory files
run: ${{ matrix.ls || 'ls -lh' }} ./rust/target/release/

- name: copy cli programs to new directory
if: matrix.os == 'windows'
run: |
mkdir cli
copy ./rust/target/release/vehicle-import-cli.exe cli
copy ./rust/target/release/fastsim-cli.exe cli
- name: copy cli programs to new directory (non-windows)
if: matrix.os != 'windows'
run: |
mkdir cli
cp ./rust/target/release/vehicle-import-cli cli
cp ./rust/target/release/fastsim-cli cli
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}-cli
path: ./cli
1 change: 1 addition & 0 deletions rust/fastsim-cli/src/bin/fastsim-cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ pub fn main() -> anyhow::Result<()> {
let fastsim_api = FastSimApi::parse();

if let Some(_cyc_json_str) = fastsim_api.cyc {
// TODO: this probably could be filled out...
anyhow::bail!("Need to implement: let cyc = RustCycle::from_json(cyc_json_str)");
}
let (is_adopt_hd, adopt_hd_string, adopt_hd_has_cycle) =
Expand Down
62 changes: 55 additions & 7 deletions rust/fastsim-core/fastsim-proc-macros/src/add_pyo3_api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,73 +207,121 @@ pub fn add_pyo3_api(attr: TokenStream, item: TokenStream) -> TokenStream {
pub fn __copy__(&self) -> Self {self.clone()}
pub fn __deepcopy__(&self, _memo: &PyDict) -> Self {self.clone()}

/// Read (deserialize) an object from a resource file packaged with the `fastsim-core` crate
///
/// # Arguments:
///
/// * `filepath`: `str | pathlib.Path` - Filepath, relative to the top of the `resources` folder, from which to read the object
///
#[staticmethod]
#[pyo3(name = "from_resource")]
pub fn from_resource_py(filepath: &PyAny) -> anyhow::Result<Self> {
Self::from_resource(PathBuf::extract(filepath)?)
}

/// Write (serialize) an object to a file.
/// Supported file extensions are listed in [`ACCEPTED_BYTE_FORMATS`](`SerdeAPI::ACCEPTED_BYTE_FORMATS`).
/// Creates a new file if it does not already exist, otherwise truncates the existing file.
///
/// # Arguments
///
/// * `filepath`: `str | pathlib.Path` - The filepath at which to write the object
///
#[pyo3(name = "to_file")]
pub fn to_file_py(&self, filepath: &PyAny) -> anyhow::Result<()> {
self.to_file(PathBuf::extract(filepath)?)
}

/// Read (deserialize) an object from a file.
/// Supported file extensions are listed in [`ACCEPTED_BYTE_FORMATS`](`SerdeAPI::ACCEPTED_BYTE_FORMATS`).
///
/// # Arguments:
///
/// * `filepath`: `str | pathlib.Path` - The filepath from which to read the object
///
#[staticmethod]
#[pyo3(name = "from_file")]
pub fn from_file_py(filepath: &PyAny) -> anyhow::Result<Self> {
Self::from_file(PathBuf::extract(filepath)?)
}

/// Write (serialize) an object into a string
///
/// # Arguments:
///
/// * `format`: `str` - The target format, any of those listed in [`ACCEPTED_STR_FORMATS`](`SerdeAPI::ACCEPTED_STR_FORMATS`)
///
#[pyo3(name = "to_str")]
pub fn to_str_py(&self, format: &str) -> anyhow::Result<String> {
self.to_str(format)
}

/// Read (deserialize) an object from a string
///
/// # Arguments:
///
/// * `contents`: `str` - The string containing the object data
/// * `format`: `str` - The source format, any of those listed in [`ACCEPTED_STR_FORMATS`](`SerdeAPI::ACCEPTED_STR_FORMATS`)
///
#[staticmethod]
#[pyo3(name = "from_str")]
pub fn from_str_py(contents: &str, format: &str) -> anyhow::Result<Self> {
Self::from_str(contents, format)
}

/// JSON serialization method.
/// Write (serialize) an object to a JSON string
#[pyo3(name = "to_json")]
pub fn to_json_py(&self) -> anyhow::Result<String> {
self.to_json()
}

/// Read (deserialize) an object to a JSON string
///
/// # Arguments
///
/// * `json_str`: `str` - JSON-formatted string to deserialize from
///
#[staticmethod]
/// JSON deserialization method.
#[pyo3(name = "from_json")]
pub fn from_json_py(json_str: &str) -> anyhow::Result<Self> {
Self::from_json(json_str)
}

/// YAML serialization method.
/// Write (serialize) an object to a YAML string
#[pyo3(name = "to_yaml")]
pub fn to_yaml_py(&self) -> anyhow::Result<String> {
self.to_yaml()
}

/// Read (deserialize) an object from a YAML string
///
/// # Arguments
///
/// * `yaml_str`: `str` - YAML-formatted string to deserialize from
///
#[staticmethod]
/// YAML deserialization method.
#[pyo3(name = "from_yaml")]
pub fn from_yaml_py(yaml_str: &str) -> anyhow::Result<Self> {
Self::from_yaml(yaml_str)
}

/// bincode serialization method.
/// Write (serialize) an object to bincode-encoded `bytes`
#[pyo3(name = "to_bincode")]
pub fn to_bincode_py<'py>(&self, py: Python<'py>) -> anyhow::Result<&'py PyBytes> {
Ok(PyBytes::new(py, &self.to_bincode()?))
}

/// Read (deserialize) an object from bincode-encoded `bytes`
///
/// # Arguments
///
/// * `encoded`: `bytes` - Encoded bytes to deserialize from
///
#[staticmethod]
/// bincode deserialization method.
#[pyo3(name = "from_bincode")]
pub fn from_bincode_py(encoded: &PyBytes) -> anyhow::Result<Self> {
Self::from_bincode(encoded.as_bytes())
}

});

let impl_block = quote! {
Expand Down
96 changes: 49 additions & 47 deletions rust/fastsim-core/src/cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ impl RustCycleCache {
pub fn from_dict(dict: &PyDict) -> anyhow::Result<Self> {
let time_s = Array::from_vec(PyAny::get_item(dict, "time_s")?.extract()?);
let cyc_len = time_s.len();
Ok(Self {
let mut cyc = Self {
time_s,
mps: Array::from_vec(PyAny::get_item(dict, "mps")?.extract()?),
grade: if let Ok(value) = PyAny::get_item(dict, "grade") {
Expand All @@ -514,7 +514,9 @@ impl RustCycleCache {
},
name: PyAny::get_item(dict, "name").and_then(String::extract).unwrap_or_default(),
orphaned: false,
})
};
cyc.init()?;
Ok(cyc)
}
pub fn to_dict<'py>(&self, py: Python<'py>) -> anyhow::Result<&'py PyDict> {
Expand Down Expand Up @@ -632,7 +634,6 @@ impl SerdeAPI for RustCycle {
// is this enough, or do I have to copy paste in the whole to_cache mathod?
const CACHE_FOLDER: &'static str = &"cycles";

// TODO: make this get called somewhere
fn init(&mut self) -> anyhow::Result<()> {
ensure!(!self.is_empty(), "Deserialized cycle is empty");
let cyc_len = self.len();
Expand Down Expand Up @@ -670,31 +671,6 @@ impl SerdeAPI for RustCycle {
Ok(())
}

fn from_reader<R: std::io::Read>(rdr: R, format: &str) -> anyhow::Result<Self> {
Ok(
match format.trim_start_matches('.').to_lowercase().as_str() {
"yaml" | "yml" => serde_yaml::from_reader(rdr)?,
"json" => serde_json::from_reader(rdr)?,
"bin" => bincode::deserialize_from(rdr)?,
"csv" => {
// Create empty cycle to be populated
let mut cyc = Self::default();
let mut rdr = csv::Reader::from_reader(rdr);
for result in rdr.deserialize() {
cyc.push(result?);
}
cyc
}
_ => {
bail!(
"Unsupported format {format:?}, must be one of {:?}",
Self::ACCEPTED_BYTE_FORMATS
)
}
},
)
}

fn to_str(&self, format: &str) -> anyhow::Result<String> {
Ok(
match format.trim_start_matches('.').to_lowercase().as_str() {
Expand All @@ -718,15 +694,42 @@ impl SerdeAPI for RustCycle {
/// Note that using this method to instantiate a RustCycle from CSV, rather
/// than the `from_csv_str` method, sets the cycle name to an empty string
fn from_str<S: AsRef<str>>(contents: S, format: &str) -> anyhow::Result<Self> {
match format.trim_start_matches('.').to_lowercase().as_str() {
"yaml" | "yml" => Self::from_yaml(contents),
"json" => Self::from_json(contents),
"csv" => Self::from_csv_str(contents, "".to_string()),
_ => bail!(
"Unsupported format {format:?}, must be one of {:?}",
Self::ACCEPTED_STR_FORMATS
),
}
Ok(
match format.trim_start_matches('.').to_lowercase().as_str() {
"yaml" | "yml" => Self::from_yaml(contents)?,
"json" => Self::from_json(contents)?,
"csv" => Self::from_csv_str(contents, "".to_string())?,
_ => bail!(
"Unsupported format {format:?}, must be one of {:?}",
Self::ACCEPTED_STR_FORMATS
),
},
)
}

fn from_reader<R: std::io::Read>(rdr: R, format: &str) -> anyhow::Result<Self> {
let mut deserialized = match format.trim_start_matches('.').to_lowercase().as_str() {
"yaml" | "yml" => serde_yaml::from_reader(rdr)?,
"json" => serde_json::from_reader(rdr)?,
"bin" => bincode::deserialize_from(rdr)?,
"csv" => {
// Create empty cycle to be populated
let mut cyc = Self::default();
let mut rdr = csv::Reader::from_reader(rdr);
for result in rdr.deserialize() {
cyc.push(result?);
}
cyc
}
_ => {
bail!(
"Unsupported format {format:?}, must be one of {:?}",
Self::ACCEPTED_BYTE_FORMATS
)
}
};
deserialized.init()?;
Ok(deserialized)
}
}

Expand All @@ -741,7 +744,7 @@ impl TryFrom<HashMap<String, Vec<f64>>> for RustCycle {
.to_owned(),
);
let cyc_len = time_s.len();
Ok(Self {
let mut cyc = Self {
time_s,
mps: Array::from_vec(
hashmap
Expand All @@ -763,7 +766,9 @@ impl TryFrom<HashMap<String, Vec<f64>>> for RustCycle {
),
name: String::default(),
orphaned: false,
})
};
cyc.init()?;
Ok(cyc)
}
}

Expand All @@ -783,14 +788,11 @@ impl RustCycle {
/// Load cycle from CSV file, parsing name from filepath
pub fn from_csv_file<P: AsRef<Path>>(filepath: P) -> anyhow::Result<Self> {
let filepath = filepath.as_ref();
let name = String::from(
filepath
.file_stem()
.and_then(OsStr::to_str)
.with_context(|| {
format!("Could not parse cycle name from filepath: {filepath:?}")
})?,
);
let name = filepath
.file_stem()
.and_then(OsStr::to_str)
.with_context(|| format!("Could not parse cycle name from filepath: {filepath:?}"))?
.to_string();
let file = File::open(filepath).with_context(|| {
if !filepath.exists() {
format!("File not found: {filepath:?}")
Expand Down
3 changes: 1 addition & 2 deletions rust/fastsim-core/src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use crate::proc_macros::{add_pyo3_api, ApproxEq};
#[cfg(feature = "pyo3")]
use crate::pyo3imports::*;

use serde_json::from_str;
use std::collections::HashMap;

/// Unit conversions that should NEVER change
Expand Down Expand Up @@ -163,7 +162,7 @@ impl SerdeAPI for AdjCoef {}
impl Default for RustLongParams {
fn default() -> Self {
let long_params_str: &str = include_str!("../resources/longparams.json");
let long_params: Self = from_str(long_params_str).unwrap();
let long_params = Self::from_json(long_params_str).unwrap();
long_params
}
}
Expand Down
Loading

0 comments on commit f6c607f

Please sign in to comment.