Rename avfutil to avflog

.. so that it won't become a dump ground.

Bug: 295168844
Test: build
Change-Id: I7e583211ac9d2db0d56fdfa0092c91a123f138ea
diff --git a/libs/avflog/Android.bp b/libs/avflog/Android.bp
new file mode 100644
index 0000000..1ddfc7a
--- /dev/null
+++ b/libs/avflog/Android.bp
@@ -0,0 +1,30 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+    name: "libavflog.defaults",
+    crate_name: "avflog",
+    host_supported: true,
+    srcs: ["src/lib.rs"],
+    edition: "2021",
+    rustlibs: [
+        "liblog_rust",
+    ],
+}
+
+rust_library {
+    name: "libavflog",
+    defaults: ["libavflog.defaults"],
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+}
+
+rust_test {
+    name: "libavflog.test",
+    defaults: ["libavflog.defaults"],
+    prefer_rlib: true,
+    test_suites: ["general-tests"],
+}
diff --git a/libs/avflog/TEST_MAPPING b/libs/avflog/TEST_MAPPING
new file mode 100644
index 0000000..921c4d8
--- /dev/null
+++ b/libs/avflog/TEST_MAPPING
@@ -0,0 +1,9 @@
+// When adding or removing tests here, don't forget to amend _all_modules list in
+// wireless/android/busytown/ath_config/configs/prod/avf/tests.gcl
+{
+  "avf-presubmit" : [
+    {
+      "name" : "libavflog.test"
+    }
+  ]
+}
diff --git a/libs/avflog/src/lib.rs b/libs/avflog/src/lib.rs
new file mode 100644
index 0000000..27c8628
--- /dev/null
+++ b/libs/avflog/src/lib.rs
@@ -0,0 +1,71 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Provides random utilities for components in AVF
+
+use log::error;
+use std::fmt::Debug;
+
+/// Convenient trait for logging an error while returning it
+pub trait LogResult<T, E> {
+    /// If this is `Err`, the error is debug-formatted and is logged via `error!`.
+    fn with_log(self) -> Result<T, E>;
+}
+
+impl<T, E: Debug> LogResult<T, E> for Result<T, E> {
+    fn with_log(self) -> Result<T, E> {
+        self.map_err(|e| {
+            error!("{e:?}");
+            e
+        })
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use log::{LevelFilter, Log, Metadata, Record};
+    use std::cell::RefCell;
+    use std::io::{Error, ErrorKind};
+
+    struct TestLogger {
+        last_log: RefCell<String>,
+    }
+    static TEST_LOGGER: TestLogger = TestLogger { last_log: RefCell::new(String::new()) };
+
+    // SAFETY: TestLogger is used only inside the test which is single-treaded.
+    unsafe impl Sync for TestLogger {}
+
+    impl Log for TestLogger {
+        fn enabled(&self, _metadata: &Metadata) -> bool {
+            true
+        }
+        fn log(&self, record: &Record) {
+            *self.last_log.borrow_mut() = format!("{}", record.args());
+        }
+        fn flush(&self) {}
+    }
+
+    #[test]
+    fn test_logresult_emits_error_log() {
+        log::set_logger(&TEST_LOGGER).unwrap();
+        log::set_max_level(LevelFilter::Info);
+
+        let e = Error::from(ErrorKind::NotFound);
+        let res: Result<(), Error> = Err(e).with_log();
+
+        assert!(res.is_err());
+        assert_eq!(TEST_LOGGER.last_log.borrow().as_str(), "Kind(NotFound)");
+    }
+}