Jelajahi Sumber

feat: Add a pretty printing Debug impl for `Fdt`

Wesley Norris 2 tahun lalu
induk
melakukan
0c2fb5b9ce
4 mengubah file dengan 126 tambahan dan 4 penghapusan
  1. 4 1
      Cargo.toml
  2. 21 2
      src/lib.rs
  3. 1 1
      src/parsing.rs
  4. 100 0
      src/pretty_print.rs

+ 4 - 1
Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "fdt"
-version = "0.1.4"
+version = "0.1.5"
 authors = ["Wesley Norris <repnop@outlook.com>"]
 edition = "2018"
 
@@ -13,4 +13,7 @@ keywords = ["devicetree", "fdt", "dt"]
 categories = ["embedded", "no-std"]
 readme = "README.md"
 
+[features]
+pretty-printing = []
+
 [dependencies]

+ 21 - 2
src/lib.rs

@@ -58,6 +58,9 @@ pub mod node;
 mod parsing;
 pub mod standard_nodes;
 
+#[cfg(feature = "pretty-printing")]
+mod pretty_print;
+
 use node::MemoryReservation;
 use parsing::{BigEndianU32, CStr, FdtData};
 use standard_nodes::{Aliases, Chosen, Cpu, Memory, MemoryRegion, Root};
@@ -87,12 +90,28 @@ impl core::fmt::Display for FdtError {
 }
 
 /// A flattened devicetree located somewhere in memory
-#[derive(Debug, Clone, Copy)]
+///
+/// Note on `Debug` impl: by default the `Debug` impl of this struct will not
+/// print any useful information, if you would like a best-effort tree print
+/// which looks similar to `dtc`'s output, enable the `pretty-printing` feature
+#[derive(Clone, Copy)]
 pub struct Fdt<'a> {
     data: &'a [u8],
     header: FdtHeader,
 }
 
+impl core::fmt::Debug for Fdt<'_> {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        #[cfg(feature = "pretty-printing")]
+        pretty_print::print_node(f, self.root().node, 0)?;
+
+        #[cfg(not(feature = "pretty-printing"))]
+        f.debug_struct("Fdt").finish_non_exhaustive()?;
+
+        Ok(())
+    }
+}
+
 #[derive(Debug, Clone, Copy)]
 #[repr(C)]
 struct FdtHeader {
@@ -266,7 +285,7 @@ impl<'a> Fdt<'a> {
     /// one of the strings inside of `with`
     pub fn find_compatible(&self, with: &[&str]) -> Option<node::FdtNode<'_, 'a>> {
         self.all_nodes().find(|n| {
-            n.compatible().and_then(|compats| compats.all().find(|c| with.contains(&c))).is_some()
+            n.compatible().and_then(|compats| compats.all().find(|c| with.contains(c))).is_some()
         })
     }
 

+ 1 - 1
src/parsing.rs

@@ -17,7 +17,7 @@ impl<'a> CStr<'a> {
     }
 
     pub fn as_str(&self) -> Option<&'a str> {
-        core::str::from_utf8(&self.0).ok()
+        core::str::from_utf8(self.0).ok()
     }
 }
 

+ 100 - 0
src/pretty_print.rs

@@ -0,0 +1,100 @@
+// This Source Code Form is subject to the terms of the Mozilla Public License,
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can
+// obtain one at https://mozilla.org/MPL/2.0/.
+
+pub fn print_node(
+    f: &mut core::fmt::Formatter<'_>,
+    node: crate::node::FdtNode<'_, '_>,
+    n_spaces: usize,
+) -> core::fmt::Result {
+    write!(f, "{:width$}", ' ', width = n_spaces)?;
+    writeln!(f, "{} {{", if node.name.is_empty() { "/" } else { node.name })?;
+    let mut were_props = false;
+    for prop in node.properties() {
+        were_props = true;
+
+        match prop.name {
+            "reg" => {
+                write!(f, "{:width$}reg = <", ' ', width = n_spaces + 4)?;
+                for (i, reg) in node.reg().unwrap().enumerate() {
+                    if i > 0 {
+                        write!(f, " ")?;
+                    }
+
+                    match reg.size {
+                        Some(size) => {
+                            write!(f, "{:#x} {:#x}", reg.starting_address as usize, size)?
+                        }
+                        None => write!(f, "{:#x}", reg.starting_address as usize)?,
+                    }
+                }
+                writeln!(f, ">")?;
+            }
+            "compatible" => writeln!(
+                f,
+                "{:width$}compatible = {:?}",
+                ' ',
+                prop.as_str().unwrap(),
+                width = n_spaces + 4
+            )?,
+            name if name.contains("-cells") => {
+                writeln!(
+                    f,
+                    "{:width$}{} = <{:#x}>",
+                    ' ',
+                    name,
+                    prop.as_usize().unwrap(),
+                    width = n_spaces + 4
+                )?;
+            }
+            _ => match prop.as_str() {
+                Some(value)
+                    if (!value.is_empty() && value.chars().all(|c| c.is_ascii_graphic()))
+                        || prop.value == [0] =>
+                {
+                    writeln!(f, "{:width$}{} = {:?}", ' ', prop.name, value, width = n_spaces + 4)?
+                }
+                _ => match prop.value.len() {
+                    4 | 8 => writeln!(
+                        f,
+                        "{:width$}{} = <{:#x}>",
+                        ' ',
+                        prop.name,
+                        prop.as_usize().unwrap(),
+                        width = n_spaces + 4
+                    )?,
+                    _ => writeln!(
+                        f,
+                        "{:width$}{} = {:?}",
+                        ' ',
+                        prop.name,
+                        prop.value,
+                        width = n_spaces + 4
+                    )?,
+                },
+            },
+        }
+    }
+
+    if node.children().next().is_some() && were_props {
+        writeln!(f)?;
+    }
+
+    let mut first = true;
+    for child in node.children() {
+        if !first {
+            writeln!(f)?;
+        }
+
+        print_node(f, child, n_spaces + 4)?;
+        first = false;
+    }
+
+    if n_spaces > 0 {
+        write!(f, "{:width$}", ' ', width = n_spaces)?;
+    }
+
+    writeln!(f, "}};")?;
+
+    Ok(())
+}