Repository: andrewdavidmackenzie/pigg
Pull request #31 (2024-05-18 19:39:51)

Implement load and save


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index ee388bb7..471014eb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1283,6 +1283,12 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
 [[package]]
 name = "jni"
 version = "0.21.1"
@@ -1884,6 +1890,8 @@ dependencies = [
  "iced_native",
  "rppal",
  "serde",
+ "serde_json",
+ "tempfile",
  "tokio",
 ]
 
@@ -2123,6 +2131,12 @@ dependencies = [
  "unicode-script",
 ]
 
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
 [[package]]
 name = "same-file"
 version = "1.0.6"
@@ -2183,6 +2197,17 @@ dependencies = [
  "syn 2.0.63",
 ]
 
+[[package]]
+name = "serde_json"
+version = "1.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
 [[package]]
 name = "simd-adler32"
 version = "0.3.7"
@@ -2364,6 +2389,18 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "tempfile"
+version = "3.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "rustix",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "termcolor"
 version = "1.4.1"
diff --git a/Cargo.toml b/Cargo.toml
index d2a7deb5..ed0a6dfb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -37,3 +37,7 @@ iced_aw = { version = "0.9.3", default-features = false, features = ["tabs", "ca
 iced_native = { version = "0.10.3", optional = true }
 tokio = { version = "1", features = ["sync"], optional = true }
 serde = { version = "1.0.202", features = ["derive"], optional = true }
+serde_json = "1.0.117"
+
+[dev-dependencies]
+tempfile = "3"
\ No newline at end of file
diff --git a/src/gpio/mod.rs b/src/gpio/mod.rs
index d70fc096..a4952a0f 100644
--- a/src/gpio/mod.rs
+++ b/src/gpio/mod.rs
@@ -1,6 +1,8 @@
 mod pin_descriptions;
 
+use std::fs::File;
 use std::io;
+use std::io::{BufReader, Write};
 use serde::{Deserialize, Serialize};
 use pin_descriptions::*;
 
@@ -58,17 +60,21 @@ pub struct GPIOConfig {
 impl GPIOConfig {
     #[cfg(feature = "gui")]
     #[allow(dead_code)] // "pi" build enables piglet which doesn't use this :-( TODO
-    pub fn load(_filename: &str)  -> io::Result<GPIOConfig> {
-        // TODO
-        Ok(GPIOConfig::default())
+    // TODO take AsPath/AsRef etc
+    pub fn load(filename: &str)  -> io::Result<GPIOConfig> {
+        let file = File::open(filename)?;
+        let reader = BufReader::new(file);
+        let config = serde_json::from_reader(reader)?;
+        Ok(config)
     }
 
     // TODO this will be used when we add a SAVE button or similar
     #[cfg(feature = "gui")]
     #[allow(dead_code)]
-    pub fn save(_filename: &str) -> io::Result<()> {
-        // TODO unimplemented
-        Ok(())
+    pub fn save(&self, filename: &str) -> io::Result<()> {
+        let mut file = File::create(filename)?;
+        let contents = serde_json::to_string(self)?;
+        file.write_all(contents.as_bytes())
     }
 }
 
@@ -84,11 +90,58 @@ pub struct GPIOState {
 
 #[cfg(test)]
 mod test {
-    use crate::gpio;
+    use std::fs;
+    use std::fs::File;
+    use std::io::Write;
+    use std::path::PathBuf;
+    use tempfile::tempdir;
+    use crate::gpio::{GPIOConfig, PinFunction};
 
     #[test]
     fn create_a_config() {
-        let config = gpio::GPIOConfig::default();
+        let config = GPIOConfig::default();
         assert!(config.configured_pins.is_empty());
     }
+
+    #[test]
+    fn load_one_pin_config() {
+        let pin_config = r#"{"configured_pins":[[1,"Input"]]}"#;
+        let output_dir = tempdir().expect("Could not create a tempdir").into_path();
+        let test_file = output_dir.join("test.piggui");
+        let mut file = File::create(&test_file).expect("Could not create test file");
+        file.write_all(pin_config.as_bytes()).expect("Could not write to test file");
+        let config = GPIOConfig::load(test_file.to_str().unwrap()).unwrap();
+        assert_eq!(config.configured_pins.len(), 1);
+        assert_eq!(config.configured_pins[0].0, 1);
+        assert_eq!(config.configured_pins[0].1, PinFunction::Input);
+    }
+
+    #[test]
+    fn load_test_file() {
+        let root = std::env::var("CARGO_MANIFEST_DIR")
+            .expect("Could not get manifest dir");
+        let mut path = PathBuf::from(root);
+        path = path.join("tests/one_pin_config.piggui");
+        let config = GPIOConfig::load(path.to_str().unwrap()).unwrap();
+        assert_eq!(config.configured_pins.len(), 1);
+        assert_eq!(config.configured_pins[0].0, 1);
+        assert_eq!(config.configured_pins[0].1, PinFunction::Input);
+    }
+
+    #[test]
+    fn save_one_pin_config() {
+        let config = GPIOConfig {
+            configured_pins: vec!((1, PinFunction::Input))
+        };
+
+        let output_dir = tempdir().expect("Could not create a tempdir").into_path();
+        let test_file = output_dir.join("test.piggui");
+
+        config.save(test_file.to_str().unwrap()).unwrap();
+
+        let pin_config = r#"{"configured_pins":[[1,"Input"]]}"#;
+        let contents = fs::read_to_string(test_file)
+            .expect("Could not read test file");
+        assert_eq!(contents, pin_config);
+    }
 }
\ No newline at end of file
diff --git a/src/piggui.rs b/src/piggui.rs
index 95d2c262..fb563d12 100644
--- a/src/piggui.rs
+++ b/src/piggui.rs
@@ -168,7 +168,7 @@ fn pin_view(
         left_pin_row = left_pin_row.push(
             button(Text::new(pair[0].board_pin_number.to_string()).size(20))
                 .padding(10)
-                .width(Length::Fixed(40 as f32))
+                .width(Length::Fixed(40f32))
                 .style(get_button_style())
                 .on_press(Message::Activate),
         );
@@ -185,7 +185,7 @@ fn pin_view(
         right_pin_row = right_pin_row.push(
             iced::widget::Button::new(Text::new(pair[1].board_pin_number.to_string()).size(20))
                 .padding(10)
-                .width(Length::Fixed(40 as f32))
+                .width(Length::Fixed(40f32))
                 .style(get_button_style())
                 .on_press(Message::Activate),
         );


Test patch:
diff --git a/tests/one_pin_config.piggui b/tests/one_pin_config.piggui
new file mode 100644
index 00000000..1e1150fc
--- /dev/null
+++ b/tests/one_pin_config.piggui
@@ -0,0 +1,3 @@
+{
+    "configured_pins":[[1,"Input"]]
+}