Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion rust/agama-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ fn expand_set_fn(field_name: &Vec<Ident>) -> TokenStream2 {
}

quote! {
fn set(&mut self, attr: &str, value: SettingValue) -> Result<(), &'static str> {
fn set(&mut self, attr: &str, value: crate::settings::SettingValue) -> Result<(), &'static str> {
match attr {
#(stringify!(#field_name) => self.#field_name = value.try_into()?,)*
_ => return Err("unknown attribute")
Expand Down
161 changes: 7 additions & 154 deletions rust/agama-lib/src/install_settings.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
//! Configuration settings handling
//!
//! This module implements the mechanisms to load and store the installation settings.
use crate::network::NetworkSettings;
use crate::{
network::NetworkSettings,
storage::StorageSettings,
software::SoftwareSettings,
users::UserSettings
};
use crate::settings::{SettingObject, SettingValue, Settings};
use agama_derive::Settings;
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::default::Default;
use std::str::FromStr;

Expand Down Expand Up @@ -169,154 +172,4 @@ impl Settings for InstallSettings {
storage.merge(other_storage);
}
}
}

/// User settings
///
/// Holds the user settings for the installation.
#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UserSettings {
#[serde(rename = "user")]
pub first_user: Option<FirstUserSettings>,
pub root: Option<RootUserSettings>,
}

impl Settings for UserSettings {
fn set(&mut self, attr: &str, value: SettingValue) -> Result<(), &'static str> {
if let Some((ns, id)) = attr.split_once('.') {
match ns {
"user" => {
let first_user = self.first_user.get_or_insert(Default::default());
first_user.set(id, value)?
}
"root" => {
let root_user = self.root.get_or_insert(Default::default());
root_user.set(id, value)?
}
_ => return Err("unknown attribute"),
}
}
Ok(())
}

fn merge(&mut self, other: &Self) {
if let Some(other_first_user) = &other.first_user {
let first_user = self.first_user.get_or_insert(Default::default());
first_user.merge(other_first_user);
}

if let Some(other_root) = &other.root {
let root = self.root.get_or_insert(Default::default());
root.merge(other_root);
}
}
}

/// First user settings
///
/// Holds the settings for the first user.
#[derive(Debug, Default, Settings, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FirstUserSettings {
/// First user's full name
pub full_name: Option<String>,
/// First user's username
pub user_name: Option<String>,
/// First user's password (in clear text)
pub password: Option<String>,
/// Whether auto-login should enabled or not
pub autologin: Option<bool>,
}

/// Root user settings
///
/// Holds the settings for the root user.
#[derive(Debug, Default, Settings, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RootUserSettings {
/// Root's password (in clear text)
#[serde(skip_serializing)]
pub password: Option<String>,
/// Root SSH public key
pub ssh_public_key: Option<String>,
}

/// Storage settings for installation
#[derive(Debug, Default, Settings, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct StorageSettings {
/// Whether LVM should be enabled
pub lvm: Option<bool>,
/// Encryption password for the storage devices (in clear text)
pub encryption_password: Option<String>,
/// Devices to use in the installation
#[collection_setting]
pub devices: Vec<Device>,
}

/// Device to use in the installation
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Device {
/// Device name (e.g., "/dev/sda")
pub name: String,
}

impl TryFrom<SettingObject> for Device {
type Error = &'static str;

fn try_from(value: SettingObject) -> Result<Self, Self::Error> {
match value.0.get("name") {
Some(name) => Ok(Device {
name: name.clone().try_into()?,
}),
None => Err("'name' key not found"),
}
}
}

/// Software settings for installation
#[derive(Debug, Default, Settings, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SoftwareSettings {
/// ID of the product to install (e.g., "ALP", "Tumbleweed", etc.)
pub product: Option<String>,
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_user_settings_merge() {
let mut user1 = UserSettings::default();
let user2 = UserSettings {
first_user: Some(FirstUserSettings {
full_name: Some("Jane Doe".to_string()),
..Default::default()
}),
root: Some(RootUserSettings {
password: Some("nots3cr3t".to_string()),
..Default::default()
}),
};
user1.merge(&user2);
let first_user = user1.first_user.unwrap();
assert_eq!(first_user.full_name, Some("Jane Doe".to_string()));
let root_user = user1.root.unwrap();
assert_eq!(root_user.password, Some("nots3cr3t".to_string()));
}

#[test]
fn test_merge() {
let mut user1 = FirstUserSettings::default();
let user2 = FirstUserSettings {
full_name: Some("Jane Doe".to_owned()),
autologin: Some(true),
..Default::default()
};
user1.merge(&user2);
assert_eq!(user1.full_name.unwrap(), "Jane Doe")
}
}
}
25 changes: 25 additions & 0 deletions rust/agama-lib/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
//! # Interacting with Agama
//!
//! This library offers an API to interact with Agama services. At this point, the library allows:
//!
//! * Reading and writing [installation settings](install_settings::InstallSettings).
//! * Monitoring the [progress](progress).
//! * Triggering actions through the [manager] (e.g., starting installation).
//!
//! ## Handling installation settings
//!
//! Let's have a look to the components that are involved when dealing with the installation
//! settings, as it is the most complex part of the library. The code is organized in a set of
//! modules, one for each topic, like [network], [software], and so on.
//!
//! Each of those modules contains, at least:
//!
//! * A settings model: it is a representation of the installation settings for the given topic. It
//! is expected to implement the [serde::Serialize], [serde::Deserialize] and [settings::Settings]
//! traits.
//! * A store: it is the responsible for reading/writing the settings to the service. Usually, it
//! relies on a D-Bus client for communicating with the service, although it could implement that
//! logic itself. Note: we are considering defining a trait for stores too.
//!
//! As said, those modules might implement additional stuff, like specific types, clients, etc.

pub mod error;
pub mod install_settings;
pub mod manager;
Expand Down
6 changes: 4 additions & 2 deletions rust/agama-lib/src/network.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
//! Implements support for handling the network settings

mod client;
mod model;
mod settings;
mod proxies;
mod store;
pub mod types;

pub use client::NetworkClient;
pub use model::NetworkSettings;
pub use settings::NetworkSettings;
pub use store::NetworkStore;
2 changes: 1 addition & 1 deletion rust/agama-lib/src/network/client.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::model::{NetworkConnection, WirelessSettings};
use super::settings::{NetworkConnection, WirelessSettings};
use super::types::SSID;
use crate::error::ServiceError;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! Configuration network settings handling
//!
//! This module implements the mechanisms to load and store the installation settings.
//! Representation of the network settings

use crate::settings::{SettingObject, Settings};
use agama_derive::Settings;
use serde::{Deserialize, Serialize};
Expand Down
Loading