diff --git a/.github/workflows/maturin_upload_pypi.yml b/.github/workflows/maturin_upload_pypi.yml index f379cf4..66e2a3d 100644 --- a/.github/workflows/maturin_upload_pypi.yml +++ b/.github/workflows/maturin_upload_pypi.yml @@ -7,11 +7,6 @@ name: Upload to PyPI on: push: - branches: - - main - - master - tags: - - '*' pull_request: workflow_dispatch: @@ -102,11 +97,31 @@ jobs: name: wheels path: dist + check_clippy_python_bindings: + name: Check clippy for Python bindings + runs-on: ubuntu-latest + + steps: + - name: Checkout reposistory + uses: actions/checkout@v4 + + - name: Setup Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Setup clippy + run: rustup component add clippy + + - name: Run clippy + run: cargo clippy --all-targets --features python_bindings -- -D warnings + release: name: Release runs-on: ubuntu-latest if: "startsWith(github.ref, 'refs/tags/')" - needs: [linux, windows, macos, sdist] + needs: [linux, windows, macos, sdist, check_clippy_python_bindings] steps: - uses: actions/download-artifact@v3 with: diff --git a/.github/workflows/publish_crate.yml b/.github/workflows/publish_crate.yml index cfdb811..0acd914 100644 --- a/.github/workflows/publish_crate.yml +++ b/.github/workflows/publish_crate.yml @@ -4,8 +4,8 @@ name: Build and upload Rust crate on: [push, pull_request] jobs: - build_rust: - name: Build Rust stuff and run Rust tests + check_fmt: + name: Check format runs-on: ubuntu-latest steps: @@ -21,18 +21,60 @@ jobs: - name: Check format run: cargo fmt --check + check_clippy: + name: Check clippy + runs-on: ubuntu-latest + + steps: + - name: Checkout reposistory + uses: actions/checkout@v4 + + - name: Setup Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: Setup clippy run: rustup component add clippy - name: Run clippy - run: cargo clippy --all-targets --all-features -- -D warnings + run: cargo clippy --all-targets -- -D warnings - - name: Build Rust package - run: cargo build --release --workspace + run_tests: + name: Run tests + runs-on: ubuntu-latest + + steps: + - name: Checkout reposistory + uses: actions/checkout@v4 + + - name: Setup Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true - - name: Build Rust tests + - name: Run tests run: cargo test --workspace + publish: + name: Publish + runs-on: ubuntu-latest + + steps: + - name: Checkout reposistory + uses: actions/checkout@v4 + + - name: Setup Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Build Rust package + run: cargo build --release --workspace + - name: Publish dry run if: github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/') run: cargo publish --dry-run diff --git a/CHANGELOG.md b/CHANGELOG.md index 8017c2a..af1c681 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- `pyo3` is no longer needed to use this crate as Rust-only library. + ## [2.3.0] - 2023-11-05 ### Added diff --git a/Cargo.toml b/Cargo.toml index c543dc1..01b2aa2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,10 +23,8 @@ crate-type = ["cdylib", "staticlib", "rlib"] [dependencies] regex = "1.9.5" -# pyo3 = { version = "0.19.0", optional = true } -pyo3 = { version = "0.19.0", optional = false } +pyo3 = { version = "0.19.0", optional = true } lazy_static = "1.4.0" [features] -python_bindings = [] -# python_bindings = ["dep:pyo3"] +python_bindings = ["dep:pyo3"] diff --git a/src/mapfile_parser/frontends/progress.py b/src/mapfile_parser/frontends/progress.py index 9a1bdb6..733083e 100644 --- a/src/mapfile_parser/frontends/progress.py +++ b/src/mapfile_parser/frontends/progress.py @@ -32,7 +32,7 @@ def processArguments(args: argparse.Namespace): asmPath: Path = args.asmpath nonmatchingsPath: Path = args.nonmatchingspath pathIndex: int = args.path_index - debugging: bool = args.debugging + debugging: bool = args.debugging #! @deprecated exit(doProgress(mapPath, asmPath, nonmatchingsPath, pathIndex=pathIndex, debugging=debugging)) @@ -43,6 +43,6 @@ def addSubparser(subparser: argparse._SubParsersAction[argparse.ArgumentParser]) parser.add_argument("asmpath", help="Path to asm folder", type=Path) parser.add_argument("nonmatchingspath", help="Path to nonmatchings folder", type=Path) parser.add_argument("-i", "--path-index", help="Specify the index to start reading the file paths. Defaults to 2", type=int, default=2) - parser.add_argument("-d", "--debugging", help="Enable debugging prints", action="store_true") + parser.add_argument("-d", "--debugging", help="Enable debugging prints. This option is deprecated", action="store_true") parser.set_defaults(func=processArguments) diff --git a/src/rs/file.rs b/src/rs/file.rs index f219ffc..a5286c4 100644 --- a/src/rs/file.rs +++ b/src/rs/file.rs @@ -10,40 +10,27 @@ use std::hash::{Hash, Hasher}; use crate::{symbol, utils}; #[cfg(feature = "python_bindings")] -use pyo3::class::basic::CompareOp; use pyo3::prelude::*; -#[cfg(feature = "python_bindings")] -use std::collections::hash_map::DefaultHasher; #[derive(Debug, Clone)] -#[pyclass(module = "mapfile_parser", sequence)] +#[cfg_attr(feature = "python_bindings", pyclass(module = "mapfile_parser"))] pub struct File { - #[pyo3(get, set, name = "_filepath_internal")] - // TODO: pyo3 exposes this as str, need to fix somehow pub filepath: PathBuf, - #[pyo3(get, set)] pub vram: u64, - #[pyo3(get, set)] pub size: u64, - #[pyo3(get, set, name = "sectionType")] pub section_type: String, - #[pyo3(get, set)] pub vrom: Option, - #[pyo3(get, set)] pub align: Option, - // #[pyo3(get, set, name = "_symbols")] pub symbols: Vec, } -#[pymethods] impl File { - #[new] pub fn new( filepath: PathBuf, vram: u64, @@ -52,7 +39,7 @@ impl File { vrom: Option, align: Option, ) -> Self { - File { + Self { filepath, vram, size, @@ -63,24 +50,10 @@ impl File { } } - #[getter] - #[pyo3(name = "isNoloadSection")] pub fn is_noload_section(&self) -> bool { utils::is_noload_section(&self.section_type) } - #[cfg(feature = "python_bindings")] - // ! @deprecated - #[pyo3(name = "getName")] - fn get_name(&self) -> PathBuf { - self.filepath - .with_extension("") - .components() - .skip(2) - .collect() - } - - #[pyo3(name = "findSymbolByName")] pub fn find_symbol_by_name(&self, sym_name: &str) -> Option { for sym in &self.symbols { if sym.name == sym_name { @@ -90,7 +63,6 @@ impl File { None } - #[pyo3(name = "findSymbolByVramOrVrom")] pub fn find_symbol_by_vram_or_vrom(&self, address: u64) -> Option<(symbol::Symbol, i64)> { let mut prev_sym: Option<&symbol::Symbol> = None; @@ -155,8 +127,6 @@ impl File { None } - #[staticmethod] - #[pyo3(name = "toCsvHeader", signature=(print_vram=true))] pub fn to_csv_header(print_vram: bool) -> String { let mut ret = String::new(); @@ -167,7 +137,6 @@ impl File { ret } - #[pyo3(name = "toCsv", signature=(print_vram=true))] pub fn to_csv(&self, print_vram: bool) -> String { let mut ret = String::new(); @@ -208,94 +177,13 @@ impl File { ret } - #[staticmethod] - #[pyo3(name = "printCsvHeader", signature=(print_vram=true))] pub fn print_csv_header(print_vram: bool) { println!("{}", Self::to_csv_header(print_vram)); } - #[pyo3(name = "printAsCsv", signature=(print_vram=true))] pub fn print_as_csv(&self, print_vram: bool) { println!("{}", self.to_csv(print_vram)); } - - /* - def toJson(self, humanReadable: bool=True) -> dict[str, Any]: - fileDict: dict[str, Any] = { - "filepath": str(self.filepath), - "sectionType": self.sectionType, - "vram": self.serializeVram(humanReadable=humanReadable), - "size": self.serializeSize(humanReadable=humanReadable), - "vrom": self.serializeVrom(humanReadable=humanReadable), - } - - symbolsList = [] - for symbol in self._symbols: - symbolsList.append(symbol.toJson(humanReadable=humanReadable)) - - fileDict["symbols"] = symbolsList - return fileDict - */ - - #[cfg(feature = "python_bindings")] - #[pyo3(name = "copySymbolList")] - fn copy_symbol_list(&self) -> Vec { - self.symbols.clone() - } - - #[cfg(feature = "python_bindings")] - #[pyo3(name = "setSymbolList")] - fn set_symbol_list(&mut self, new_list: Vec) { - self.symbols = new_list; - } - - #[cfg(feature = "python_bindings")] - #[pyo3(name = "appendSymbol")] - fn append_symbol(&mut self, sym: symbol::Symbol) { - self.symbols.push(sym); - } - - #[cfg(feature = "python_bindings")] - fn __iter__(slf: PyRef<'_, Self>) -> PyResult> { - let iter = SymbolVecIter { - inner: slf.symbols.clone().into_iter(), - }; - Py::new(slf.py(), iter) - } - - #[cfg(feature = "python_bindings")] - fn __getitem__(&self, index: usize) -> symbol::Symbol { - self.symbols[index].clone() - } - - #[cfg(feature = "python_bindings")] - fn __setitem__(&mut self, index: usize, element: symbol::Symbol) { - self.symbols[index] = element; - } - - #[cfg(feature = "python_bindings")] - fn __len__(&self) -> usize { - self.symbols.len() - } - - // TODO: implement __eq__ instead when PyO3 0.20 releases - #[cfg(feature = "python_bindings")] - fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> PyObject { - match op { - pyo3::class::basic::CompareOp::Eq => (self == other).into_py(py), - pyo3::class::basic::CompareOp::Ne => (self != other).into_py(py), - _ => py.NotImplemented(), - } - } - - #[cfg(feature = "python_bindings")] - fn __hash__(&self) -> isize { - let mut hasher = DefaultHasher::new(); - self.hash(&mut hasher); - hasher.finish() as isize - } - - // TODO: __str__ and __repr__ } impl File { @@ -367,19 +255,242 @@ impl Hash for File { } #[cfg(feature = "python_bindings")] -#[pyclass] -struct SymbolVecIter { - inner: std::vec::IntoIter, -} +#[allow(non_snake_case)] +pub(crate) mod python_bindings { + use pyo3::class::basic::CompareOp; + use pyo3::prelude::*; + + use std::path::PathBuf; + + // Required to call the `.hash` and `.finish` methods, which are defined on traits. + use std::hash::{Hash, Hasher}; + + use crate::symbol; + + use std::collections::hash_map::DefaultHasher; + + #[pymethods] + impl super::File { + #[new] + pub fn py_new( + filepath: PathBuf, + vram: u64, + size: u64, + section_type: &str, + vrom: Option, + align: Option, + ) -> Self { + Self::new(filepath, vram, size, section_type, vrom, align) + } -#[cfg(feature = "python_bindings")] -#[pymethods] -impl SymbolVecIter { - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { - slf + /* Getters and setters */ + + // TODO: pyo3 exposes this as str, need to fix somehow + #[getter] + fn get__filepath_internal(&self) -> PyResult { + Ok(self.filepath.clone()) + } + + #[setter] + fn set__filepath_internal(&mut self, value: PathBuf) -> PyResult<()> { + self.filepath = value; + Ok(()) + } + + #[getter] + fn get_vram(&self) -> PyResult { + Ok(self.vram) + } + + #[setter] + fn set_vram(&mut self, value: u64) -> PyResult<()> { + self.vram = value; + Ok(()) + } + + #[getter] + fn get_size(&self) -> PyResult { + Ok(self.size) + } + + #[setter] + fn set_size(&mut self, value: u64) -> PyResult<()> { + self.size = value; + Ok(()) + } + + #[getter] + fn get_sectionType(&self) -> PyResult { + Ok(self.section_type.clone()) + } + + #[setter] + fn set_sectionType(&mut self, value: String) -> PyResult<()> { + self.section_type = value; + Ok(()) + } + + #[getter] + fn get_vrom(&self) -> PyResult> { + Ok(self.vrom) + } + + #[setter] + fn set_vrom(&mut self, value: Option) -> PyResult<()> { + self.vrom = value; + Ok(()) + } + + #[getter] + fn get_align(&self) -> PyResult> { + Ok(self.align) + } + + #[setter] + fn set_align(&mut self, value: Option) -> PyResult<()> { + self.align = value; + Ok(()) + } + + /* + #[getter] + fn get__symbols(&self) -> PyResult> { + Ok(self.symbols) + } + + #[setter] + fn set__symbols(&mut self, value: Vec) -> PyResult<()> { + self.symbols = value; + Ok(()) + } + */ + + #[getter] + pub fn isNoloadSection(&self) -> bool { + self.is_noload_section() + } + + /* Methods */ + + // ! @deprecated + fn getName(&self) -> PathBuf { + self.filepath + .with_extension("") + .components() + .skip(2) + .collect() + } + + pub fn findSymbolByName(&self, sym_name: &str) -> Option { + self.find_symbol_by_name(sym_name) + } + + pub fn findSymbolByVramOrVrom(&self, address: u64) -> Option<(symbol::Symbol, i64)> { + self.find_symbol_by_vram_or_vrom(address) + } + + #[staticmethod] + #[pyo3(signature=(print_vram=true))] + pub fn toCsvHeader(print_vram: bool) -> String { + Self::to_csv_header(print_vram) + } + + #[pyo3(signature=(print_vram=true))] + pub fn toCsv(&self, print_vram: bool) -> String { + self.to_csv(print_vram) + } + + #[staticmethod] + #[pyo3(signature=(print_vram=true))] + pub fn printCsvHeader(print_vram: bool) { + Self::print_csv_header(print_vram) + } + + #[pyo3(signature=(print_vram=true))] + pub fn printAsCsv(&self, print_vram: bool) { + self.print_as_csv(print_vram) + } + + /* + def toJson(self, humanReadable: bool=True) -> dict[str, Any]: + fileDict: dict[str, Any] = { + "filepath": str(self.filepath), + "sectionType": self.sectionType, + "vram": self.serializeVram(humanReadable=humanReadable), + "size": self.serializeSize(humanReadable=humanReadable), + "vrom": self.serializeVrom(humanReadable=humanReadable), + } + + symbolsList = [] + for symbol in self._symbols: + symbolsList.append(symbol.toJson(humanReadable=humanReadable)) + + fileDict["symbols"] = symbolsList + return fileDict + */ + + fn copySymbolList(&self) -> Vec { + self.symbols.clone() + } + + fn setSymbolList(&mut self, new_list: Vec) { + self.symbols = new_list; + } + + fn appendSymbol(&mut self, sym: symbol::Symbol) { + self.symbols.push(sym); + } + + fn __iter__(slf: PyRef<'_, Self>) -> PyResult> { + let iter = SymbolVecIter { + inner: slf.symbols.clone().into_iter(), + }; + Py::new(slf.py(), iter) + } + + fn __getitem__(&self, index: usize) -> symbol::Symbol { + self.symbols[index].clone() + } + + fn __setitem__(&mut self, index: usize, element: symbol::Symbol) { + self.symbols[index] = element; + } + + fn __len__(&self) -> usize { + self.symbols.len() + } + + // TODO: implement __eq__ instead when PyO3 0.20 releases + fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> PyObject { + match op { + pyo3::class::basic::CompareOp::Eq => (self == other).into_py(py), + pyo3::class::basic::CompareOp::Ne => (self != other).into_py(py), + _ => py.NotImplemented(), + } + } + + fn __hash__(&self) -> isize { + let mut hasher = DefaultHasher::new(); + self.hash(&mut hasher); + hasher.finish() as isize + } + + // TODO: __str__ and __repr__ + } + + #[pyclass] + struct SymbolVecIter { + inner: std::vec::IntoIter, } - fn __next__(mut slf: PyRefMut<'_, Self>) -> Option { - slf.inner.next() + #[pymethods] + impl SymbolVecIter { + fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + slf + } + + fn __next__(mut slf: PyRefMut<'_, Self>) -> Option { + slf.inner.next() + } } } diff --git a/src/rs/found_symbol_info.rs b/src/rs/found_symbol_info.rs index cc5feb8..3bbf36f 100644 --- a/src/rs/found_symbol_info.rs +++ b/src/rs/found_symbol_info.rs @@ -4,34 +4,36 @@ use crate::{file, symbol}; use std::fmt::Write; +#[cfg(feature = "python_bindings")] use pyo3::prelude::*; #[derive(Debug, Clone)] -#[pyclass(module = "mapfile_parser")] +#[cfg_attr(feature = "python_bindings", pyclass(module = "mapfile_parser"))] pub struct FoundSymbolInfo { - #[pyo3(get, set)] pub file: file::File, - #[pyo3(get, set)] pub symbol: symbol::Symbol, - #[pyo3(get, set)] pub offset: i64, } -#[pymethods] impl FoundSymbolInfo { - #[new] - #[pyo3(signature=(file, symbol, offset=0))] pub fn new(file: file::File, symbol: symbol::Symbol, offset: i64) -> Self { - FoundSymbolInfo { + Self { file, symbol, offset, } } - #[pyo3(name = "getAsStr")] + pub fn new_default(file: file::File, symbol: symbol::Symbol) -> Self { + Self { + file, + symbol, + offset: 0, + } + } + pub fn get_as_str(&self) -> String { format!( "'{0}' (VRAM: {1}, VROM: {2}, SIZE: {3}, {4})", @@ -43,7 +45,6 @@ impl FoundSymbolInfo { ) } - #[pyo3(name = "getAsStrPlusOffset")] pub fn get_as_str_plus_offset(&self, sym_name: Option) -> String { let mut message; @@ -62,12 +63,66 @@ impl FoundSymbolInfo { } } -impl FoundSymbolInfo { - pub fn new_default(file: file::File, symbol: symbol::Symbol) -> Self { - FoundSymbolInfo { - file, - symbol, - offset: 0, +#[cfg(feature = "python_bindings")] +#[allow(non_snake_case)] +pub(crate) mod python_bindings { + use pyo3::prelude::*; + + use crate::{file, symbol}; + + #[pymethods] + impl super::FoundSymbolInfo { + #[new] + #[pyo3(signature=(file, symbol, offset=0))] + pub fn py_new(file: file::File, symbol: symbol::Symbol, offset: i64) -> Self { + Self::new(file, symbol, offset) + } + + /* Getters and setters */ + + #[getter] + fn get_file(&self) -> PyResult { + Ok(self.file.clone()) + } + + #[setter] + fn set_file(&mut self, value: file::File) -> PyResult<()> { + self.file = value; + Ok(()) + } + + #[getter] + fn get_symbol(&self) -> PyResult { + Ok(self.symbol.clone()) + } + + #[setter] + fn set_symbol(&mut self, value: symbol::Symbol) -> PyResult<()> { + self.symbol = value; + Ok(()) + } + + #[getter] + fn get_offset(&self) -> PyResult { + Ok(self.offset) + } + + #[setter] + fn set_offset(&mut self, value: i64) -> PyResult<()> { + self.offset = value; + Ok(()) + } + + /* Methods */ + + #[pyo3(name = "getAsStr")] + pub fn getAsStr(&self) -> String { + self.get_as_str() + } + + #[pyo3(name = "getAsStrPlusOffset")] + pub fn getAsStrPlusOffset(&self, sym_name: Option) -> String { + self.get_as_str_plus_offset(sym_name) } } } diff --git a/src/rs/lib.rs b/src/rs/lib.rs index 194dd07..c7e81ba 100644 --- a/src/rs/lib.rs +++ b/src/rs/lib.rs @@ -23,6 +23,7 @@ pub use symbol_comparison_info::SymbolComparisonInfo; #[macro_use] extern crate lazy_static; +#[cfg(feature = "python_bindings")] #[pyo3::prelude::pymodule] fn mapfile_parser( _py: pyo3::prelude::Python<'_>, diff --git a/src/rs/mapfile.rs b/src/rs/mapfile.rs index c20f487..f9b1b2a 100644 --- a/src/rs/mapfile.rs +++ b/src/rs/mapfile.rs @@ -8,6 +8,7 @@ use std::path::PathBuf; use regex::*; +#[cfg(feature = "python_bindings")] use pyo3::prelude::*; use crate::{ @@ -25,20 +26,21 @@ lazy_static! { #[derive(Debug, Clone)] // TODO: sequence? -#[pyclass(module = "mapfile_parser")] +#[cfg_attr(feature = "python_bindings", pyclass(module = "mapfile_parser"))] pub struct MapFile { pub segments_list: Vec, + #[cfg(feature = "python_bindings")] #[pyo3(get, set)] debugging: bool, } -#[pymethods] impl MapFile { - #[new] pub fn new() -> Self { MapFile { segments_list: Vec::new(), + + #[cfg(feature = "python_bindings")] debugging: false, } } @@ -52,7 +54,6 @@ impl MapFile { - GNU ld - clang ld.lld */ - #[pyo3(name = "readMapFile")] pub fn read_map_file(&mut self, map_path: PathBuf) { let map_contents = utils::read_file_contents(&map_path); @@ -70,7 +71,6 @@ impl MapFile { - GNU ld - clang ld.lld */ - #[pyo3(name = "parseMapContents")] pub fn parse_map_contents(&mut self, map_contents: String) { let regex_lld_header = Regex::new(r"\s+VMA\s+LMA\s+Size\s+Align\s+Out\s+In\s+Symbol").unwrap(); @@ -88,7 +88,6 @@ impl MapFile { The `mapContents` argument must contain the contents of a GNU ld mapfile. */ - #[pyo3(name = "parseMapContentsGNU")] pub fn parse_map_contents_gnu(&mut self, map_contents: String) { // TODO: maybe move somewhere else? let regex_file_data_entry = Regex::new(r"^\s+(?P
\.[^\s]+)\s+(?P0x[^\s]+)\s+(?P0x[^\s]+)\s+(?P[^\s]+)$").unwrap(); @@ -281,7 +280,6 @@ impl MapFile { The `mapContents` argument must contain the contents of a clang ld.lld mapfile. */ - #[pyo3(name = "parseMapContentsLLD")] pub fn parse_map_contents_lld(&mut self, map_contents: String) { let map_data = map_contents; @@ -424,12 +422,9 @@ impl MapFile { } } - #[pyo3(name = "filterBySectionType")] pub fn filter_by_section_type(&self, section_type: &str) -> MapFile { let mut new_map_file = MapFile::new(); - new_map_file.debugging = self.debugging; - for segment in &self.segments_list { let new_segment = segment.filter_by_section_type(section_type); @@ -441,12 +436,9 @@ impl MapFile { new_map_file } - #[pyo3(name = "getEveryFileExceptSectionType")] pub fn get_every_file_except_section_type(&self, section_type: &str) -> MapFile { let mut new_map_file = MapFile::new(); - new_map_file.debugging = self.debugging; - for segment in &self.segments_list { let new_segment = segment.get_every_file_except_section_type(section_type); @@ -458,7 +450,6 @@ impl MapFile { new_map_file } - #[pyo3(name = "findSymbolByName")] pub fn find_symbol_by_name( &self, sym_name: &str, @@ -472,7 +463,6 @@ impl MapFile { None } - #[pyo3(name = "findSymbolByVramOrVrom")] pub fn find_symbol_by_vram_or_vrom( &self, address: u64, @@ -486,7 +476,6 @@ impl MapFile { None } - #[pyo3(name = "findLowestDifferingSymbol")] pub fn find_lowest_differing_symbol( &self, other_map_file: MapFile, @@ -522,12 +511,9 @@ impl MapFile { None } - #[pyo3(name = "mixFolders")] pub fn mix_folders(&self) -> MapFile { let mut new_map_file = MapFile::new(); - new_map_file.debugging = self.debugging; - for segment in &self.segments_list { new_map_file.segments_list.push(segment.mix_folders()); } @@ -535,7 +521,6 @@ impl MapFile { new_map_file } - #[pyo3(name = "getProgress", signature = (asm_path, nonmatchings, aliases=HashMap::new(), path_index=2))] pub fn get_progress( &self, asm_path: PathBuf, @@ -607,7 +592,6 @@ impl MapFile { (total_stats, progress_per_folder) } - #[pyo3(name = "compareFilesAndSymbols", signature=(other_map_file, *, check_other_on_self=true))] /// Useful for finding bss reorders pub fn compare_files_and_symbols( &self, @@ -678,7 +662,6 @@ impl MapFile { comp_info } - #[pyo3(name = "toCsv", signature=(print_vram=true, skip_without_symbols=true))] pub fn to_csv(&self, print_vram: bool, skip_without_symbols: bool) -> String { let mut ret = file::File::to_csv_header(print_vram) + "\n"; @@ -689,7 +672,6 @@ impl MapFile { ret } - #[pyo3(name = "toCsvSymbols")] pub fn to_csv_symbols(&self) -> String { let mut ret = String::new(); @@ -702,56 +684,13 @@ impl MapFile { ret } - #[pyo3(name = "printAsCsv", signature=(print_vram=true, skip_without_symbols=true))] pub fn print_as_csv(&self, print_vram: bool, skip_without_symbols: bool) { print!("{}", self.to_csv(print_vram, skip_without_symbols)); } - #[pyo3(name = "printSymbolsCsv")] pub fn print_symbols_csv(&self) { print!("{}", self.to_csv_symbols()); } - - #[cfg(feature = "python_bindings")] - #[pyo3(name = "copySegmentList")] - fn copy_segment_list(&self) -> Vec { - self.segments_list.clone() - } - - #[cfg(feature = "python_bindings")] - #[pyo3(name = "setSegmentList")] - fn set_segment_list(&mut self, new_list: Vec) { - self.segments_list = new_list; - } - - #[cfg(feature = "python_bindings")] - #[pyo3(name = "appendSegment")] - fn append_segment(&mut self, segment: segment::Segment) { - self.segments_list.push(segment); - } - - #[cfg(feature = "python_bindings")] - fn __iter__(slf: PyRef<'_, Self>) -> PyResult> { - let iter = SegmentVecIter { - inner: slf.segments_list.clone().into_iter(), - }; - Py::new(slf.py(), iter) - } - - #[cfg(feature = "python_bindings")] - fn __getitem__(&self, index: usize) -> segment::Segment { - self.segments_list[index].clone() - } - - #[cfg(feature = "python_bindings")] - fn __setitem__(&mut self, index: usize, element: segment::Segment) { - self.segments_list[index] = element; - } - - #[cfg(feature = "python_bindings")] - fn __len__(&self) -> usize { - self.segments_list.len() - } } impl MapFile { @@ -778,19 +717,163 @@ impl Default for MapFile { } #[cfg(feature = "python_bindings")] -#[pyclass] -struct SegmentVecIter { - inner: std::vec::IntoIter, -} +#[allow(non_snake_case)] +pub(crate) mod python_bindings { + use pyo3::prelude::*; -#[cfg(feature = "python_bindings")] -#[pymethods] -impl SegmentVecIter { - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { - slf + use std::collections::HashMap; + use std::path::PathBuf; + + use crate::{file, found_symbol_info, maps_comparison_info, progress_stats, segment, symbol}; + + #[pymethods] + impl super::MapFile { + #[new] + pub fn py_new() -> Self { + Self::new() + } + + pub fn readMapFile(&mut self, map_path: PathBuf) { + self.read_map_file(map_path) + } + + pub fn parseMapContents(&mut self, map_contents: String) { + self.parse_map_contents(map_contents) + } + + pub fn parseMapContentsGNU(&mut self, map_contents: String) { + self.parse_map_contents_gnu(map_contents) + } + + /** + Parses the contents of a clang ld.lld map. + + The `mapContents` argument must contain the contents of a clang ld.lld mapfile. + */ + #[pyo3(name = "parseMapContentsLLD")] + pub fn parseMapContentsLLD(&mut self, map_contents: String) { + self.parse_map_contents_lld(map_contents) + } + + pub fn filterBySectionType(&self, section_type: &str) -> Self { + self.filter_by_section_type(section_type) + } + + pub fn getEveryFileExceptSectionType(&self, section_type: &str) -> Self { + self.get_every_file_except_section_type(section_type) + } + + pub fn findSymbolByName( + &self, + sym_name: &str, + ) -> Option { + self.find_symbol_by_name(sym_name) + } + + pub fn findSymbolByVramOrVrom( + &self, + address: u64, + ) -> Option { + self.find_symbol_by_vram_or_vrom(address) + } + + pub fn findLowestDifferingSymbol( + &self, + other_map_file: Self, + ) -> Option<(symbol::Symbol, file::File, Option)> { + self.find_lowest_differing_symbol(other_map_file) + } + + pub fn mixFolders(&self) -> Self { + self.mix_folders() + } + + #[pyo3(signature = (asm_path, nonmatchings, aliases=HashMap::new(), path_index=2))] + pub fn getProgress( + &self, + asm_path: PathBuf, + nonmatchings: PathBuf, + aliases: HashMap, + path_index: usize, + ) -> ( + progress_stats::ProgressStats, + HashMap, + ) { + self.get_progress(asm_path, nonmatchings, aliases, path_index) + } + + #[pyo3(signature=(other_map_file, *, check_other_on_self=true))] + pub fn compareFilesAndSymbols( + &self, + other_map_file: Self, + check_other_on_self: bool, + ) -> maps_comparison_info::MapsComparisonInfo { + self.compare_files_and_symbols(other_map_file, check_other_on_self) + } + + #[pyo3(signature=(print_vram=true, skip_without_symbols=true))] + pub fn toCsv(&self, print_vram: bool, skip_without_symbols: bool) -> String { + self.to_csv(print_vram, skip_without_symbols) + } + + pub fn toCsvSymbols(&self) -> String { + self.to_csv_symbols() + } + + #[pyo3(signature=(print_vram=true, skip_without_symbols=true))] + pub fn printAsCsv(&self, print_vram: bool, skip_without_symbols: bool) { + self.print_as_csv(print_vram, skip_without_symbols) + } + + pub fn printSymbolsCsv(&self) { + self.print_symbols_csv() + } + + fn copySegmentList(&self) -> Vec { + self.segments_list.clone() + } + + fn setSegmentList(&mut self, new_list: Vec) { + self.segments_list = new_list; + } + + fn appendSegment(&mut self, segment: segment::Segment) { + self.segments_list.push(segment); + } + + fn __iter__(slf: PyRef<'_, Self>) -> PyResult> { + let iter = SegmentVecIter { + inner: slf.segments_list.clone().into_iter(), + }; + Py::new(slf.py(), iter) + } + + fn __getitem__(&self, index: usize) -> segment::Segment { + self.segments_list[index].clone() + } + + fn __setitem__(&mut self, index: usize, element: segment::Segment) { + self.segments_list[index] = element; + } + + fn __len__(&self) -> usize { + self.segments_list.len() + } } - fn __next__(mut slf: PyRefMut<'_, Self>) -> Option { - slf.inner.next() + #[pyclass] + struct SegmentVecIter { + inner: std::vec::IntoIter, + } + + #[pymethods] + impl SegmentVecIter { + fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + slf + } + + fn __next__(mut slf: PyRefMut<'_, Self>) -> Option { + slf.inner.next() + } } } diff --git a/src/rs/maps_comparison_info.rs b/src/rs/maps_comparison_info.rs index d7f8310..885f6ed 100644 --- a/src/rs/maps_comparison_info.rs +++ b/src/rs/maps_comparison_info.rs @@ -5,26 +5,22 @@ use std::collections::HashSet; use crate::{file, symbol_comparison_info}; +#[cfg(feature = "python_bindings")] use pyo3::prelude::*; #[derive(Debug, Clone)] -#[pyclass(module = "mapfile_parser")] +#[cfg_attr(feature = "python_bindings", pyclass(module = "mapfile_parser"))] pub struct MapsComparisonInfo { - #[pyo3(get, set, name = "badFiles")] pub bad_files: HashSet, - #[pyo3(get, set, name = "missingFiles")] pub missing_files: HashSet, - #[pyo3(get, set, name = "comparedList")] pub compared_list: Vec, } -#[pymethods] impl MapsComparisonInfo { - #[new] pub fn new() -> Self { - MapsComparisonInfo { + Self { bad_files: HashSet::new(), missing_files: HashSet::new(), compared_list: Vec::new(), @@ -37,3 +33,59 @@ impl Default for MapsComparisonInfo { Self::new() } } + +#[cfg(feature = "python_bindings")] +#[allow(non_snake_case)] +pub(crate) mod python_bindings { + use pyo3::prelude::*; + + use std::collections::HashSet; + + use crate::{file, symbol_comparison_info}; + + #[pymethods] + impl super::MapsComparisonInfo { + #[new] + pub fn py_new() -> Self { + Self::new() + } + + /* Getters and setters */ + + #[getter] + fn get_badFiles(&self) -> PyResult> { + Ok(self.bad_files.clone()) + } + + #[setter] + fn set_badFiles(&mut self, value: HashSet) -> PyResult<()> { + self.bad_files = value; + Ok(()) + } + + #[getter] + fn get_missingFiles(&self) -> PyResult> { + Ok(self.missing_files.clone()) + } + + #[setter] + fn set_missingFiles(&mut self, value: HashSet) -> PyResult<()> { + self.missing_files = value; + Ok(()) + } + + #[getter] + fn get_comparedList(&self) -> PyResult> { + Ok(self.compared_list.clone()) + } + + #[setter] + fn set_comparedList( + &mut self, + value: Vec, + ) -> PyResult<()> { + self.compared_list = value; + Ok(()) + } + } +} diff --git a/src/rs/progress_stats.rs b/src/rs/progress_stats.rs index 5040598..cd33bdb 100644 --- a/src/rs/progress_stats.rs +++ b/src/rs/progress_stats.rs @@ -3,21 +3,18 @@ use std::collections::HashMap; +#[cfg(feature = "python_bindings")] use pyo3::prelude::*; #[derive(Debug, Clone)] -#[pyclass(module = "mapfile_parser", sequence)] +#[cfg_attr(feature = "python_bindings", pyclass(module = "mapfile_parser"))] pub struct ProgressStats { - #[pyo3(get, set, name = "undecompedSize")] pub undecomped_size: u32, - #[pyo3(get, set, name = "decompedSize")] pub decomped_size: u32, } -#[pymethods] impl ProgressStats { - #[new] pub fn new() -> Self { Self { undecomped_size: 0, @@ -25,12 +22,10 @@ impl ProgressStats { } } - #[getter] pub fn total(&self) -> u32 { self.undecomped_size + self.decomped_size } - #[pyo3(name = "getAsFrogressEntry")] pub fn get_as_frogress_entry(&self, name: &str) -> HashMap { let mut categories: HashMap = HashMap::new(); @@ -40,8 +35,6 @@ impl ProgressStats { categories } - #[staticmethod] - #[pyo3(name = "printHeader")] pub fn print_header() { println!( "{:<28}: {:>12} / {:>8} {:>10}% ({:>20}%)", @@ -49,7 +42,7 @@ impl ProgressStats { ); } - pub fn print(&self, category: &str, total_stats: &ProgressStats) { + pub fn print(&self, category: &str, total_stats: &Self) { println!( "{:<28}: {:>12} / {:>8} {:>10.4}% ({:>8.4}% / {:>8.4}%)", category, @@ -67,3 +60,65 @@ impl Default for ProgressStats { Self::new() } } + +#[cfg(feature = "python_bindings")] +#[allow(non_snake_case)] +pub(crate) mod python_bindings { + use pyo3::prelude::*; + + use std::collections::HashMap; + + #[pymethods] + impl super::ProgressStats { + #[new] + pub fn py_new() -> Self { + Self::new() + } + + /* Getters and setters */ + + #[getter] + fn get_undecompedSize(&self) -> PyResult { + Ok(self.undecomped_size) + } + + #[setter] + fn set_undecompedSize(&mut self, value: u32) -> PyResult<()> { + self.undecomped_size = value; + Ok(()) + } + + #[getter] + fn get_decompedSize(&self) -> PyResult { + Ok(self.decomped_size) + } + + #[setter] + fn set_decompedSize(&mut self, value: u32) -> PyResult<()> { + self.decomped_size = value; + Ok(()) + } + + #[getter] + #[pyo3(name = "total")] + pub fn py_total(&self) -> u32 { + self.total() + } + + /* Methods */ + + pub fn getAsFrogressEntry(&self, name: &str) -> HashMap { + self.get_as_frogress_entry(name) + } + + #[staticmethod] + pub fn printHeader() { + Self::print_header() + } + + #[pyo3(name = "print")] + pub fn py_print(&self, category: &str, total_stats: &Self) { + self.print(category, total_stats) + } + } +} diff --git a/src/rs/segment.rs b/src/rs/segment.rs index 9aa7814..cf276a6 100644 --- a/src/rs/segment.rs +++ b/src/rs/segment.rs @@ -11,36 +11,25 @@ use std::collections::hash_map::Entry; use std::hash::{Hash, Hasher}; #[cfg(feature = "python_bindings")] -use pyo3::class::basic::CompareOp; use pyo3::prelude::*; -#[cfg(feature = "python_bindings")] -use std::collections::hash_map::DefaultHasher; #[derive(Debug, Clone)] -#[pyclass(module = "mapfile_parser")] +#[cfg_attr(feature = "python_bindings", pyclass(module = "mapfile_parser"))] pub struct Segment { - #[pyo3(get, set)] pub name: String, - #[pyo3(get, set)] pub vram: u64, - #[pyo3(get, set)] pub size: u64, - #[pyo3(get, set)] pub vrom: u64, - #[pyo3(get, set)] pub align: Option, - // #[pyo3(get, set)] pub files_list: Vec, } -#[pymethods] impl Segment { - #[new] pub fn new(name: String, vram: u64, size: u64, vrom: u64, align: Option) -> Self { Segment { name, @@ -52,8 +41,7 @@ impl Segment { } } - #[pyo3(name = "filterBySectionType")] - pub fn filter_by_section_type(&self, section_type: &str) -> Segment { + pub fn filter_by_section_type(&self, section_type: &str) -> Self { let mut new_segment = self.clone_no_filelist(); for file in &self.files_list { @@ -65,8 +53,7 @@ impl Segment { new_segment } - #[pyo3(name = "getEveryFileExceptSectionType")] - pub fn get_every_file_except_section_type(&self, section_type: &str) -> Segment { + pub fn get_every_file_except_section_type(&self, section_type: &str) -> Self { let mut new_segment = self.clone_no_filelist(); for file in &self.files_list { @@ -78,7 +65,6 @@ impl Segment { new_segment } - #[pyo3(name = "findSymbolByName")] pub fn find_symbol_by_name( &self, sym_name: &str, @@ -94,7 +80,6 @@ impl Segment { None } - #[pyo3(name = "findSymbolByVramOrVrom")] pub fn find_symbol_by_vram_or_vrom( &self, address: u64, @@ -114,8 +99,7 @@ impl Segment { None } - #[pyo3(name = "mixFolders")] - pub fn mix_folders(&self) -> Segment { + pub fn mix_folders(&self) -> Self { let mut new_segment = self.clone_no_filelist(); // > @@ -172,7 +156,6 @@ impl Segment { new_segment } - #[pyo3(name = "toCsv", signature=(print_vram=true, skip_without_symbols=true))] pub fn to_csv(&self, print_vram: bool, skip_without_symbols: bool) -> String { let mut ret = String::new(); @@ -187,7 +170,6 @@ impl Segment { ret } - #[pyo3(name = "toCsvSymbols")] pub fn to_csv_symbols(&self) -> String { let mut ret = String::new(); @@ -204,78 +186,14 @@ impl Segment { ret } - #[pyo3(name = "printAsCsv", signature=(print_vram=true, skip_without_symbols=true))] pub fn print_as_csv(&self, print_vram: bool, skip_without_symbols: bool) { print!("{}", self.to_csv(print_vram, skip_without_symbols)); } - #[pyo3(name = "printSymbolsCsv")] pub fn print_symbols_csv(&self) { print!("{}", self.to_csv_symbols()); } - #[cfg(feature = "python_bindings")] - #[pyo3(name = "copyFileList")] - fn copy_file_list(&self) -> Vec { - self.files_list.clone() - } - - #[cfg(feature = "python_bindings")] - #[pyo3(name = "setFileList")] - fn set_file_list(&mut self, new_list: Vec) { - self.files_list = new_list; - } - - #[cfg(feature = "python_bindings")] - #[pyo3(name = "appendFile")] - fn append_file(&mut self, file: file::File) { - self.files_list.push(file); - } - - #[cfg(feature = "python_bindings")] - fn __iter__(slf: PyRef<'_, Self>) -> PyResult> { - let iter = FileVecIter { - inner: slf.files_list.clone().into_iter(), - }; - Py::new(slf.py(), iter) - } - - #[cfg(feature = "python_bindings")] - fn __getitem__(&self, index: usize) -> file::File { - self.files_list[index].clone() - } - - #[cfg(feature = "python_bindings")] - fn __setitem__(&mut self, index: usize, element: file::File) { - self.files_list[index] = element; - } - - #[cfg(feature = "python_bindings")] - fn __len__(&self) -> usize { - self.files_list.len() - } - - // TODO: implement __eq__ instead when PyO3 0.20 releases - #[cfg(feature = "python_bindings")] - fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> PyObject { - match op { - pyo3::class::basic::CompareOp::Eq => (self == other).into_py(py), - pyo3::class::basic::CompareOp::Ne => (self != other).into_py(py), - _ => py.NotImplemented(), - } - } - - #[cfg(feature = "python_bindings")] - fn __hash__(&self) -> isize { - let mut hasher = DefaultHasher::new(); - self.hash(&mut hasher); - hasher.finish() as isize - } - - // TODO: __str__ and __repr__ -} - -impl Segment { pub fn new_default(name: String, vram: u64, size: u64, vrom: u64) -> Self { Segment { name, @@ -354,19 +272,203 @@ impl Hash for Segment { } #[cfg(feature = "python_bindings")] -#[pyclass] -struct FileVecIter { - inner: std::vec::IntoIter, -} +#[allow(non_snake_case)] +pub(crate) mod python_bindings { + use pyo3::class::basic::CompareOp; + use pyo3::prelude::*; -#[cfg(feature = "python_bindings")] -#[pymethods] -impl FileVecIter { - fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { - slf + use std::collections::hash_map::DefaultHasher; + + // Required to call the `.hash` and `.finish` methods, which are defined on traits. + use std::hash::{Hash, Hasher}; + + use crate::{file, found_symbol_info}; + + #[pymethods] + impl super::Segment { + #[new] + pub fn py_new(name: String, vram: u64, size: u64, vrom: u64, align: Option) -> Self { + Self::new(name, vram, size, vrom, align) + } + + /* Getters and setters */ + + #[getter] + fn get_name(&self) -> PyResult { + Ok(self.name.clone()) + } + + #[setter] + fn set_name(&mut self, value: String) -> PyResult<()> { + self.name = value; + Ok(()) + } + + #[getter] + fn get_vram(&self) -> PyResult { + Ok(self.vram) + } + + #[setter] + fn set_vram(&mut self, value: u64) -> PyResult<()> { + self.vram = value; + Ok(()) + } + + #[getter] + fn get_size(&self) -> PyResult { + Ok(self.size) + } + + #[setter] + fn set_size(&mut self, value: u64) -> PyResult<()> { + self.size = value; + Ok(()) + } + + #[getter] + fn get_vrom(&self) -> PyResult { + Ok(self.vrom) + } + + #[setter] + fn set_vrom(&mut self, value: u64) -> PyResult<()> { + self.vrom = value; + Ok(()) + } + + #[getter] + fn get_align(&self) -> PyResult> { + Ok(self.align) + } + + #[setter] + fn set_align(&mut self, value: Option) -> PyResult<()> { + self.align = value; + Ok(()) + } + + /* + #[getter] + fn get_files_list(&self) -> PyResult> { + Ok(self.files_list) + } + + #[setter] + fn set_files_list(&mut self, value: Vec) -> PyResult<()> { + self.files_list = value; + Ok(()) + } + */ + + /* Methods */ + + pub fn filterBySectionType(&self, section_type: &str) -> Self { + self.filter_by_section_type(section_type) + } + + pub fn getEveryFileExceptSectionType(&self, section_type: &str) -> Self { + self.get_every_file_except_section_type(section_type) + } + + pub fn findSymbolByName( + &self, + sym_name: &str, + ) -> Option { + self.find_symbol_by_name(sym_name) + } + + pub fn findSymbolByVramOrVrom( + &self, + address: u64, + ) -> Option { + self.find_symbol_by_vram_or_vrom(address) + } + + pub fn mixFolders(&self) -> Self { + self.mix_folders() + } + + #[pyo3(signature=(print_vram=true, skip_without_symbols=true))] + pub fn toCsv(&self, print_vram: bool, skip_without_symbols: bool) -> String { + self.to_csv(print_vram, skip_without_symbols) + } + + pub fn toCsvSymbols(&self) -> String { + self.to_csv_symbols() + } + + #[pyo3(signature=(print_vram=true, skip_without_symbols=true))] + pub fn printAsCsv(&self, print_vram: bool, skip_without_symbols: bool) { + self.print_as_csv(print_vram, skip_without_symbols) + } + + pub fn printSymbolsCsv(&self) { + self.print_symbols_csv() + } + + fn copyFileList(&self) -> Vec { + self.files_list.clone() + } + + fn setFileList(&mut self, new_list: Vec) { + self.files_list = new_list; + } + + fn appendFile(&mut self, file: file::File) { + self.files_list.push(file); + } + + fn __iter__(slf: PyRef<'_, Self>) -> PyResult> { + let iter = FileVecIter { + inner: slf.files_list.clone().into_iter(), + }; + Py::new(slf.py(), iter) + } + + fn __getitem__(&self, index: usize) -> file::File { + self.files_list[index].clone() + } + + fn __setitem__(&mut self, index: usize, element: file::File) { + self.files_list[index] = element; + } + + fn __len__(&self) -> usize { + self.files_list.len() + } + + // TODO: implement __eq__ instead when PyO3 0.20 releases + fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> PyObject { + match op { + pyo3::class::basic::CompareOp::Eq => (self == other).into_py(py), + pyo3::class::basic::CompareOp::Ne => (self != other).into_py(py), + _ => py.NotImplemented(), + } + } + + fn __hash__(&self) -> isize { + let mut hasher = DefaultHasher::new(); + self.hash(&mut hasher); + hasher.finish() as isize + } + + // TODO: __str__ and __repr__ + } + + #[pyclass] + struct FileVecIter { + inner: std::vec::IntoIter, } - fn __next__(mut slf: PyRefMut<'_, Self>) -> Option { - slf.inner.next() + #[pymethods] + impl FileVecIter { + fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + slf + } + + fn __next__(mut slf: PyRefMut<'_, Self>) -> Option { + slf.inner.next() + } } } diff --git a/src/rs/symbol.rs b/src/rs/symbol.rs index 2085fab..b3ef04c 100644 --- a/src/rs/symbol.rs +++ b/src/rs/symbol.rs @@ -5,33 +5,23 @@ use std::hash::{Hash, Hasher}; #[cfg(feature = "python_bindings")] -use pyo3::class::basic::CompareOp; use pyo3::prelude::*; -#[cfg(feature = "python_bindings")] -use std::collections::hash_map::DefaultHasher; #[derive(Debug, Clone)] -#[pyclass(module = "mapfile_parser")] +#[cfg_attr(feature = "python_bindings", pyclass(module = "mapfile_parser"))] pub struct Symbol { - #[pyo3(get)] pub name: String, - #[pyo3(get)] pub vram: u64, - #[pyo3(get, set)] pub size: Option, - #[pyo3(get, set)] pub vrom: Option, - #[pyo3(get, set)] pub align: Option, } -#[pymethods] impl Symbol { - #[new] pub fn new( name: String, vram: u64, @@ -39,7 +29,7 @@ impl Symbol { vrom: Option, align: Option, ) -> Self { - Symbol { + Self { name, vram, size, @@ -48,12 +38,20 @@ impl Symbol { } } - #[pyo3(name = "getVramStr")] + pub fn new_default(name: String, vram: u64) -> Self { + Self { + name, + vram, + size: None, + vrom: None, + align: None, + } + } + pub fn get_vram_str(&self) -> String { format!("0x{0:08X}", self.vram) } - #[pyo3(name = "getSizeStr")] pub fn get_size_str(&self) -> String { if let Some(size) = self.size { //return format!("0x{0:X}", size); @@ -62,7 +60,6 @@ impl Symbol { "None".into() } - #[pyo3(name = "getVromStr")] pub fn get_vrom_str(&self) -> String { if let Some(vrom) = self.vrom { return format!("0x{0:06X}", vrom); @@ -70,58 +67,21 @@ impl Symbol { "None".into() } - #[staticmethod] - #[pyo3(name = "toCsvHeader")] pub fn to_csv_header() -> String { "Symbol name,VRAM,Size in bytes".to_string() } - #[pyo3(name = "toCsv")] pub fn to_csv(&self) -> String { format!("{0},{1:08X},{2}", self.name, self.vram, self.get_size_str()) } - #[staticmethod] - #[pyo3(name = "printCsvHeader")] pub fn print_csv_header() { print!("{}", Self::to_csv_header()); } - #[pyo3(name = "printAsCsv")] pub fn print_as_csv(&self) { print!("{0}", self.to_csv()); } - - // TODO: implement __eq__ instead when PyO3 0.20 releases - #[cfg(feature = "python_bindings")] - fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> PyObject { - match op { - pyo3::class::basic::CompareOp::Eq => (self == other).into_py(py), - pyo3::class::basic::CompareOp::Ne => (self != other).into_py(py), - _ => py.NotImplemented(), - } - } - - #[cfg(feature = "python_bindings")] - fn __hash__(&self) -> isize { - let mut hasher = DefaultHasher::new(); - self.hash(&mut hasher); - hasher.finish() as isize - } - - // TODO: __str__ and __repr__ -} - -impl Symbol { - pub fn new_default(name: String, vram: u64) -> Self { - Symbol { - name, - vram, - size: None, - vrom: None, - align: None, - } - } } // https://doc.rust-lang.org/std/cmp/trait.Eq.html @@ -139,3 +99,135 @@ impl Hash for Symbol { self.vram.hash(state); } } + +#[cfg(feature = "python_bindings")] +#[allow(non_snake_case)] +pub(crate) mod python_bindings { + use pyo3::class::basic::CompareOp; + use pyo3::prelude::*; + + use std::collections::hash_map::DefaultHasher; + + // Required to call the `.hash` and `.finish` methods, which are defined on traits. + use std::hash::{Hash, Hasher}; + + #[pymethods] + impl super::Symbol { + #[new] + pub fn py_new( + name: String, + vram: u64, + size: Option, + vrom: Option, + align: Option, + ) -> Self { + Self::new(name, vram, size, vrom, align) + } + + /* Getters and setters */ + + #[getter] + fn get_name(&self) -> PyResult { + Ok(self.name.clone()) + } + + #[setter] + fn set_name(&mut self, value: String) -> PyResult<()> { + self.name = value; + Ok(()) + } + + #[getter] + fn get_vram(&self) -> PyResult { + Ok(self.vram) + } + + #[setter] + fn set_vram(&mut self, value: u64) -> PyResult<()> { + self.vram = value; + Ok(()) + } + + #[getter] + fn get_size(&self) -> PyResult> { + Ok(self.size) + } + + #[setter] + fn set_size(&mut self, value: Option) -> PyResult<()> { + self.size = value; + Ok(()) + } + + #[getter] + fn get_vrom(&self) -> PyResult> { + Ok(self.vrom) + } + + #[setter] + fn set_vrom(&mut self, value: Option) -> PyResult<()> { + self.vrom = value; + Ok(()) + } + + #[getter] + fn get_align(&self) -> PyResult> { + Ok(self.align) + } + + #[setter] + fn set_align(&mut self, value: Option) -> PyResult<()> { + self.align = value; + Ok(()) + } + + /* Methods */ + + pub fn getVramStr(&self) -> String { + self.get_vram_str() + } + + pub fn getSizeStr(&self) -> String { + self.get_size_str() + } + + pub fn getVromStr(&self) -> String { + self.get_vrom_str() + } + + #[staticmethod] + pub fn toCsvHeader() -> String { + Self::to_csv_header() + } + + pub fn toCsv(&self) -> String { + self.to_csv() + } + + #[staticmethod] + pub fn printCsvHeader() { + Self::print_csv_header() + } + + pub fn printAsCsv(&self) { + self.print_as_csv() + } + + // TODO: implement __eq__ instead when PyO3 0.20 releases + fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> PyObject { + match op { + pyo3::class::basic::CompareOp::Eq => (self == other).into_py(py), + pyo3::class::basic::CompareOp::Ne => (self != other).into_py(py), + _ => py.NotImplemented(), + } + } + + fn __hash__(&self) -> isize { + let mut hasher = DefaultHasher::new(); + self.hash(&mut hasher); + hasher.finish() as isize + } + + // TODO: __str__ and __repr__ + } +} diff --git a/src/rs/symbol_comparison_info.rs b/src/rs/symbol_comparison_info.rs index 595f37b..510bad1 100644 --- a/src/rs/symbol_comparison_info.rs +++ b/src/rs/symbol_comparison_info.rs @@ -2,34 +2,27 @@ /* SPDX-License-Identifier: MIT */ use crate::{file, symbol}; + +#[cfg(feature = "python_bindings")] use pyo3::prelude::*; #[derive(Debug, Clone)] -#[pyclass(module = "mapfile_parser")] +#[cfg_attr(feature = "python_bindings", pyclass(module = "mapfile_parser"))] pub struct SymbolComparisonInfo { - #[pyo3(get, set)] pub symbol: symbol::Symbol, - #[pyo3(get, set, name = "buildAddress")] pub build_address: u64, - #[pyo3(get, set, name = "buildFile")] pub build_file: Option, - #[pyo3(get, set, name = "expectedAddress")] pub expected_address: u64, - #[pyo3(get, set, name = "expectedFile")] pub expected_file: Option, - #[pyo3(get, set)] pub diff: Option, } -#[pymethods] impl SymbolComparisonInfo { - #[new] - #[pyo3(signature = (symbol, build_address, build_file, expected_address, expected_file, diff))] pub fn new( symbol: symbol::Symbol, build_address: u64, @@ -38,7 +31,7 @@ impl SymbolComparisonInfo { expected_file: Option, diff: Option, ) -> Self { - SymbolComparisonInfo { + Self { symbol, build_address, build_file, @@ -48,3 +41,98 @@ impl SymbolComparisonInfo { } } } + +#[cfg(feature = "python_bindings")] +#[allow(non_snake_case)] +pub(crate) mod python_bindings { + use pyo3::prelude::*; + + use crate::{file, symbol}; + + #[pymethods] + impl super::SymbolComparisonInfo { + #[new] + #[pyo3(signature = (symbol, build_address, build_file, expected_address, expected_file, diff))] + pub fn py_new( + symbol: symbol::Symbol, + build_address: u64, + build_file: Option, + expected_address: u64, + expected_file: Option, + diff: Option, + ) -> Self { + Self::new( + symbol, + build_address, + build_file, + expected_address, + expected_file, + diff, + ) + } + + /* Getters and setters */ + + #[getter] + fn get_symbol(&self) -> PyResult { + Ok(self.symbol.clone()) + } + #[setter] + fn set_symbol(&mut self, value: symbol::Symbol) -> PyResult<()> { + self.symbol = value; + Ok(()) + } + + #[getter] + fn get_buildAddress(&self) -> PyResult { + Ok(self.build_address) + } + #[setter] + fn set_buildAddress(&mut self, value: u64) -> PyResult<()> { + self.build_address = value; + Ok(()) + } + + #[getter] + fn get_buildFile(&self) -> PyResult> { + Ok(self.build_file.clone()) + } + #[setter] + fn set_buildFile(&mut self, value: Option) -> PyResult<()> { + self.build_file = value; + Ok(()) + } + + #[getter] + fn get_expectedAddress(&self) -> PyResult { + Ok(self.expected_address) + } + #[setter] + fn set_expectedAddress(&mut self, value: u64) -> PyResult<()> { + self.expected_address = value; + Ok(()) + } + + #[getter] + fn get_expectedFile(&self) -> PyResult> { + Ok(self.expected_file.clone()) + } + + #[setter] + fn set_expectedFile(&mut self, value: Option) -> PyResult<()> { + self.expected_file = value; + Ok(()) + } + + #[getter] + fn get_diff(&self) -> PyResult> { + Ok(self.diff) + } + + #[setter] + fn set_diff(&mut self, value: Option) -> PyResult<()> { + self.diff = value; + Ok(()) + } + } +}