Add logging for rust
Change-Id: I0537c47817f050935c4319ccb97ce4efed866f7a
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/init.rs b/aos/init.rs
index 331e74d..fe0408d 100644
--- a/aos/init.rs
+++ b/aos/init.rs
@@ -1,3 +1,44 @@
+//! AOS Initialization
+//!
+//! This module "links" the C++ library and the Rust application together.
+//! In particular it provides the [`Init`] trait which is implemented for
+//! any struct that implements [`clap::Parser`]. The reason for this is that
+//! an important part of initializing the C++ library involves setting the
+//! gFlags which get resolved dynamically thanks to their reflection API.
+//!
+//! # Examples
+//!
+//! ```no_run
+//! use aos_init::Init;
+//! use clap::Parser;
+//!
+//! #[derive(Parser, Debug)]
+//! struct App {
+//! /// Time to sleep between pings.
+//! #[arg(long, default_value_t = 10000, value_name = "MICROS")]
+//! sleep: u64,
+//! }
+//!
+//! fn main() {
+//! // Initializes AOS and returns the App struct with the parsed CLI flags
+//! let app: App = App::init();
+//! // At this point your flags are parsed and AOS is initialized.
+//! }
+//! ```
+//! You can also use [`DefaultApp`] to avoid having to specify your own CLI options if you don't
+//! need them. For example:
+//!
+//! ```no_run
+//! use aos_init::{DefaultApp, Init};
+//! use clap::Parser;
+//!
+//! fn main() {
+//! // Initializes AOS. DefaultApp doesn't have any flags to parse.
+//! let _ = DefaultApp::init();
+//! // At this point AOS is initialized and you can create event loop.
+//! }
+//!```
+
use std::{
env,
ffi::{CString, OsStr, OsString},
@@ -22,32 +63,61 @@
generate!("aos::GetCommandLineOption")
);
-/// Initializes AOS.
-pub fn init() {
- static ONCE: Once = Once::new();
- ONCE.call_once(|| {
- // We leak the `CString` with `into_raw`. It's not sound for C++ to free
- // it but `InitGoogleLogging` requries that it is long-lived.
- let argv0 = std::env::args()
- .map(|arg| CString::new(arg).expect("Arg may not have NUL"))
- .next()
- .expect("Missing argv[0]?")
- .into_raw();
- // SAFETY: argv0 is a well-defined CString.
- unsafe {
- ffi::aos::InitFromRust(argv0);
- }
- });
+// Intended to be used only from here and test_init. Don't use it anywhere else please.
+#[doc(hidden)]
+pub mod internal {
+ use super::*;
+ /// Generic initialization for production and tests.
+ ///
+ /// Sets up the C++ side of things. Notably, it doesn't setup the command line flags.
+ pub fn init() {
+ static ONCE: Once = Once::new();
+ ONCE.call_once(|| {
+ // We leak the `CString` with `into_raw`. It's not sound for C++ to free
+ // it but `InitGoogleLogging` requries that it is long-lived.
+ let argv0 = std::env::args()
+ .map(|arg| CString::new(arg).expect("Arg may not have NUL"))
+ .next()
+ .expect("Missing argv[0]?")
+ .into_raw();
+ // SAFETY: argv0 is a well-defined CString.
+ unsafe {
+ ffi::aos::InitFromRust(argv0);
+ }
+ });
+ }
}
+/// An application that doesn't need custom command line flags.
+///
+/// If you need your own command line flags, use any struct that derives [`clap::Parser`] instead.
+#[derive(Parser, Debug)]
+pub struct DefaultApp {}
+
/// Trait used to append C++ gFlags to a clap CLI.
-pub trait WithCppFlags: Parser {
+pub trait Init: Parser {
+ /// Initializes an AOS application.
+ ///
+ /// Parses the command line flags and runs the initialization logic.
+ fn init() -> Self {
+ let this = Self::parse_with_cpp_flags();
+ // Rust logs to stderr by default. Make that true for C++ as that will be easier than
+ // managing one or multiple files across FFI. We can pipe the stderr to a file to get
+ // a log file if we want.
+ CxxFlag::set_option("logtostderr", "true".as_ref())
+ .expect("Error setting C++ flag: logtostderr");
+ internal::init();
+ // Non-test initialization below
+ env_logger::init();
+ this
+ }
+
/// Parses the comannd line arguments while also setting the C++ gFlags.
fn parse_with_cpp_flags() -> Self {
Self::parse_with_cpp_flags_from(env::args_os())
}
- /// Like [`WithCppFlags::parse_with_cpp_flags`] but read from an iterator.
+ /// Like [`Init::parse_with_cpp_flags`] but read from an iterator.
fn parse_with_cpp_flags_from<I, T>(itr: I) -> Self
where
I: IntoIterator<Item = T>,
@@ -100,7 +170,7 @@
}
}
-impl<T: Parser> WithCppFlags for T {}
+impl<T: Parser> Init for T {}
#[derive(Clone)]
#[allow(unused)]
@@ -112,13 +182,19 @@
filename: String,
}
+#[derive(Debug)]
struct SetFlagError;
impl CxxFlag {
/// Sets the command gFlag to the specified value.
fn set(&self, value: &OsStr) -> Result<(), SetFlagError> {
+ Self::set_option(&self.name, value)
+ }
+
+ /// Sets the command gFlag to the specified value.
+ fn set_option(name: &str, value: &OsStr) -> Result<(), SetFlagError> {
unsafe {
- let name = CString::new(self.name.clone()).expect("Flag name may not have NUL");
+ let name = CString::new(name.clone()).expect("Flag name may not have NUL");
let value = CString::new(value.as_bytes()).expect("Arg may not have NUL");
if ffi::aos::SetCommandLineOption(name.as_ptr(), value.as_ptr()) {
Ok(())