Skip to main content

UsersFile

Struct UsersFile 

Source
pub struct UsersFile { /* private fields */ }
Expand description

The station’s user list, as a file (UsersFile).

Reached from Engine::users_file. Everything a station knows about who may log in and what they may do lives here, and this is the only route to persisting it: User objects created through Engine::new_user exist in memory until this file is saved.

The lists are PropertyObject arrays rather than typed collections, because that is what the engine returns and how it expects them to be edited. Add or remove an element with the property-object API and the change lands in the list itself; see user_list.

§Saving

Go through as_property_object_file and call PropertyObjectFile::save_file_if_modified, passing false so the engine writes the file instead of asking a person first.

Implementations§

Source§

impl UsersFile

Source

pub fn user_list(&self) -> Result<PropertyObject, Error>

The users, as an array of User objects (UsersFile.UserList).

Editing happens through this array, not through a setter: use the property-object API to append or remove an element and the station’s user list changes with it.

§Errors

Error if the COM call fails or returns an unexpected type.

Examples found in repository?
examples/users_manage.rs (line 105)
29fn main() -> Result<(), rs_teststand::Error> {
30    let engine = Engine::new()?;
31
32    // Passing no profile means the user inherits nothing.
33    let user = engine.new_user(None)?;
34    user.set_login_name("operator1")?;
35    user.set_full_name("Test Operator")?;
36    user.set_password("ts-secret")?;
37
38    println!("User: {} ({})", user.login_name()?, user.full_name()?);
39
40    // Check a credential without handling the stored value.
41    println!(
42        "  password 'ts-secret' valid: {}",
43        user.validate_password("ts-secret")?
44    );
45    println!(
46        "  password 'wrong' valid:     {}",
47        user.validate_password("wrong")?
48    );
49
50    println!("  privilege checks:");
51    report_privileges(
52        &user,
53        &[
54            UserPrivilege::Operate,
55            UserPrivilege::Execute,
56            UserPrivilege::Develop,
57            UserPrivilege::Debug,
58            UserPrivilege::EditUsers,
59        ],
60    )?;
61
62    // A privilege can also be named by its full path, which is how a leaf
63    // inside a category is reached.
64    println!(
65        "  Debug.RunSelectedTests:  {}",
66        user.has_privilege_named("Debug.RunSelectedTests")?
67    );
68
69    // The privilege tree is nested: categories hold the individual rights.
70    let privileges = user.privileges()?;
71    let count = privileges.get_num_sub_properties("")?;
72    println!("  privilege tree ({count} categories):");
73    for index in 0..count {
74        let name = privileges.get_nth_sub_property_name("", index, 0)?;
75        let members = privileges
76            .get_property_object(&name, 0)?
77            .get_num_sub_properties("")?;
78        println!("    {name:<12} {members} member(s)");
79    }
80
81    // The station itself, read-only.
82    println!("\nStation:");
83    match engine.current_user()? {
84        Some(current) => println!("  logged in as {}", current.login_name()?),
85        None => println!("  nobody logged in (usual when login is not required)"),
86    }
87    println!(
88        "  is 'operator1' a real account here? {}",
89        engine.user_name_exists("operator1")?
90    );
91
92    // Where accounts actually live. Everything above this point exists only in
93    // memory: `new_user` builds a User, it does not enrol one. A user becomes
94    // part of the station when it is inserted into this file's list and the
95    // file is written.
96    let users_file = engine.users_file()?;
97    let file = users_file.as_property_object_file()?;
98    println!(
99        "
100Users file:"
101    );
102    println!("  path            {}", file.path()?);
103    println!(
104        "  users           {}",
105        users_file.user_list()?.get_num_elements()?
106    );
107    println!(
108        "  groups          {}",
109        users_file.user_group_list()?.get_num_elements()?
110    );
111    println!(
112        "  profiles        {}",
113        users_file.user_profile_list()?.get_num_elements()?
114    );
115
116    // Deliberately not run: it would edit the station this example is reading.
117    // The two calls a host makes to enrol the user built above are
118    //
119    //     users_file.user_list()?.set_num_elements(count + 1, 0)?;   // then fill
120    //     file.save_file_if_modified(false)?;                        // then write
121    //
122    // `false` matters: with `true` the engine puts a save dialog on screen, and
123    // a host with no operator would wait for an answer that never comes.
124    println!(
125        "
126  (this example does not write; enrolling a user would alter the station)"
127    );
128
129    Ok(())
130}
Source

pub fn user_group_list(&self) -> Result<PropertyObject, Error>

The user groups (UsersFile.UserGroupList).

§Errors

Error if the COM call fails or returns an unexpected type.

Examples found in repository?
examples/users_manage.rs (line 109)
29fn main() -> Result<(), rs_teststand::Error> {
30    let engine = Engine::new()?;
31
32    // Passing no profile means the user inherits nothing.
33    let user = engine.new_user(None)?;
34    user.set_login_name("operator1")?;
35    user.set_full_name("Test Operator")?;
36    user.set_password("ts-secret")?;
37
38    println!("User: {} ({})", user.login_name()?, user.full_name()?);
39
40    // Check a credential without handling the stored value.
41    println!(
42        "  password 'ts-secret' valid: {}",
43        user.validate_password("ts-secret")?
44    );
45    println!(
46        "  password 'wrong' valid:     {}",
47        user.validate_password("wrong")?
48    );
49
50    println!("  privilege checks:");
51    report_privileges(
52        &user,
53        &[
54            UserPrivilege::Operate,
55            UserPrivilege::Execute,
56            UserPrivilege::Develop,
57            UserPrivilege::Debug,
58            UserPrivilege::EditUsers,
59        ],
60    )?;
61
62    // A privilege can also be named by its full path, which is how a leaf
63    // inside a category is reached.
64    println!(
65        "  Debug.RunSelectedTests:  {}",
66        user.has_privilege_named("Debug.RunSelectedTests")?
67    );
68
69    // The privilege tree is nested: categories hold the individual rights.
70    let privileges = user.privileges()?;
71    let count = privileges.get_num_sub_properties("")?;
72    println!("  privilege tree ({count} categories):");
73    for index in 0..count {
74        let name = privileges.get_nth_sub_property_name("", index, 0)?;
75        let members = privileges
76            .get_property_object(&name, 0)?
77            .get_num_sub_properties("")?;
78        println!("    {name:<12} {members} member(s)");
79    }
80
81    // The station itself, read-only.
82    println!("\nStation:");
83    match engine.current_user()? {
84        Some(current) => println!("  logged in as {}", current.login_name()?),
85        None => println!("  nobody logged in (usual when login is not required)"),
86    }
87    println!(
88        "  is 'operator1' a real account here? {}",
89        engine.user_name_exists("operator1")?
90    );
91
92    // Where accounts actually live. Everything above this point exists only in
93    // memory: `new_user` builds a User, it does not enrol one. A user becomes
94    // part of the station when it is inserted into this file's list and the
95    // file is written.
96    let users_file = engine.users_file()?;
97    let file = users_file.as_property_object_file()?;
98    println!(
99        "
100Users file:"
101    );
102    println!("  path            {}", file.path()?);
103    println!(
104        "  users           {}",
105        users_file.user_list()?.get_num_elements()?
106    );
107    println!(
108        "  groups          {}",
109        users_file.user_group_list()?.get_num_elements()?
110    );
111    println!(
112        "  profiles        {}",
113        users_file.user_profile_list()?.get_num_elements()?
114    );
115
116    // Deliberately not run: it would edit the station this example is reading.
117    // The two calls a host makes to enrol the user built above are
118    //
119    //     users_file.user_list()?.set_num_elements(count + 1, 0)?;   // then fill
120    //     file.save_file_if_modified(false)?;                        // then write
121    //
122    // `false` matters: with `true` the engine puts a save dialog on screen, and
123    // a host with no operator would wait for an answer that never comes.
124    println!(
125        "
126  (this example does not write; enrolling a user would alter the station)"
127    );
128
129    Ok(())
130}
Source

pub fn user_profile_list(&self) -> Result<PropertyObject, Error>

The user profiles (UsersFile.UserProfileList).

§Errors

Error if the COM call fails or returns an unexpected type.

Examples found in repository?
examples/users_manage.rs (line 113)
29fn main() -> Result<(), rs_teststand::Error> {
30    let engine = Engine::new()?;
31
32    // Passing no profile means the user inherits nothing.
33    let user = engine.new_user(None)?;
34    user.set_login_name("operator1")?;
35    user.set_full_name("Test Operator")?;
36    user.set_password("ts-secret")?;
37
38    println!("User: {} ({})", user.login_name()?, user.full_name()?);
39
40    // Check a credential without handling the stored value.
41    println!(
42        "  password 'ts-secret' valid: {}",
43        user.validate_password("ts-secret")?
44    );
45    println!(
46        "  password 'wrong' valid:     {}",
47        user.validate_password("wrong")?
48    );
49
50    println!("  privilege checks:");
51    report_privileges(
52        &user,
53        &[
54            UserPrivilege::Operate,
55            UserPrivilege::Execute,
56            UserPrivilege::Develop,
57            UserPrivilege::Debug,
58            UserPrivilege::EditUsers,
59        ],
60    )?;
61
62    // A privilege can also be named by its full path, which is how a leaf
63    // inside a category is reached.
64    println!(
65        "  Debug.RunSelectedTests:  {}",
66        user.has_privilege_named("Debug.RunSelectedTests")?
67    );
68
69    // The privilege tree is nested: categories hold the individual rights.
70    let privileges = user.privileges()?;
71    let count = privileges.get_num_sub_properties("")?;
72    println!("  privilege tree ({count} categories):");
73    for index in 0..count {
74        let name = privileges.get_nth_sub_property_name("", index, 0)?;
75        let members = privileges
76            .get_property_object(&name, 0)?
77            .get_num_sub_properties("")?;
78        println!("    {name:<12} {members} member(s)");
79    }
80
81    // The station itself, read-only.
82    println!("\nStation:");
83    match engine.current_user()? {
84        Some(current) => println!("  logged in as {}", current.login_name()?),
85        None => println!("  nobody logged in (usual when login is not required)"),
86    }
87    println!(
88        "  is 'operator1' a real account here? {}",
89        engine.user_name_exists("operator1")?
90    );
91
92    // Where accounts actually live. Everything above this point exists only in
93    // memory: `new_user` builds a User, it does not enrol one. A user becomes
94    // part of the station when it is inserted into this file's list and the
95    // file is written.
96    let users_file = engine.users_file()?;
97    let file = users_file.as_property_object_file()?;
98    println!(
99        "
100Users file:"
101    );
102    println!("  path            {}", file.path()?);
103    println!(
104        "  users           {}",
105        users_file.user_list()?.get_num_elements()?
106    );
107    println!(
108        "  groups          {}",
109        users_file.user_group_list()?.get_num_elements()?
110    );
111    println!(
112        "  profiles        {}",
113        users_file.user_profile_list()?.get_num_elements()?
114    );
115
116    // Deliberately not run: it would edit the station this example is reading.
117    // The two calls a host makes to enrol the user built above are
118    //
119    //     users_file.user_list()?.set_num_elements(count + 1, 0)?;   // then fill
120    //     file.save_file_if_modified(false)?;                        // then write
121    //
122    // `false` matters: with `true` the engine puts a save dialog on screen, and
123    // a host with no operator would wait for an answer that never comes.
124    println!(
125        "
126  (this example does not write; enrolling a user would alter the station)"
127    );
128
129    Ok(())
130}
Source

pub fn reload_from_disk(&self) -> Result<(), Error>

Re-reads the file from disk (UsersFile.ReloadFromDisk).

Everything read from this file beforehand is stale afterwards. The engine documents that references to the user list and to individual users are out of date once this returns, so drop what you are holding and read it again rather than reusing it. Consuming &self cannot express that, since the file object itself stays valid; only what came out of it does not.

§Errors

Error if the COM call fails.

Source

pub fn as_property_object_file(&self) -> Result<PropertyObjectFile, Error>

The file view of this object (UsersFile.AsPropertyObjectFile).

Where the path lives, and where saving happens.

§Errors

Error if the COM call fails or returns an unexpected type.

Examples found in repository?
examples/users_manage.rs (line 97)
29fn main() -> Result<(), rs_teststand::Error> {
30    let engine = Engine::new()?;
31
32    // Passing no profile means the user inherits nothing.
33    let user = engine.new_user(None)?;
34    user.set_login_name("operator1")?;
35    user.set_full_name("Test Operator")?;
36    user.set_password("ts-secret")?;
37
38    println!("User: {} ({})", user.login_name()?, user.full_name()?);
39
40    // Check a credential without handling the stored value.
41    println!(
42        "  password 'ts-secret' valid: {}",
43        user.validate_password("ts-secret")?
44    );
45    println!(
46        "  password 'wrong' valid:     {}",
47        user.validate_password("wrong")?
48    );
49
50    println!("  privilege checks:");
51    report_privileges(
52        &user,
53        &[
54            UserPrivilege::Operate,
55            UserPrivilege::Execute,
56            UserPrivilege::Develop,
57            UserPrivilege::Debug,
58            UserPrivilege::EditUsers,
59        ],
60    )?;
61
62    // A privilege can also be named by its full path, which is how a leaf
63    // inside a category is reached.
64    println!(
65        "  Debug.RunSelectedTests:  {}",
66        user.has_privilege_named("Debug.RunSelectedTests")?
67    );
68
69    // The privilege tree is nested: categories hold the individual rights.
70    let privileges = user.privileges()?;
71    let count = privileges.get_num_sub_properties("")?;
72    println!("  privilege tree ({count} categories):");
73    for index in 0..count {
74        let name = privileges.get_nth_sub_property_name("", index, 0)?;
75        let members = privileges
76            .get_property_object(&name, 0)?
77            .get_num_sub_properties("")?;
78        println!("    {name:<12} {members} member(s)");
79    }
80
81    // The station itself, read-only.
82    println!("\nStation:");
83    match engine.current_user()? {
84        Some(current) => println!("  logged in as {}", current.login_name()?),
85        None => println!("  nobody logged in (usual when login is not required)"),
86    }
87    println!(
88        "  is 'operator1' a real account here? {}",
89        engine.user_name_exists("operator1")?
90    );
91
92    // Where accounts actually live. Everything above this point exists only in
93    // memory: `new_user` builds a User, it does not enrol one. A user becomes
94    // part of the station when it is inserted into this file's list and the
95    // file is written.
96    let users_file = engine.users_file()?;
97    let file = users_file.as_property_object_file()?;
98    println!(
99        "
100Users file:"
101    );
102    println!("  path            {}", file.path()?);
103    println!(
104        "  users           {}",
105        users_file.user_list()?.get_num_elements()?
106    );
107    println!(
108        "  groups          {}",
109        users_file.user_group_list()?.get_num_elements()?
110    );
111    println!(
112        "  profiles        {}",
113        users_file.user_profile_list()?.get_num_elements()?
114    );
115
116    // Deliberately not run: it would edit the station this example is reading.
117    // The two calls a host makes to enrol the user built above are
118    //
119    //     users_file.user_list()?.set_num_elements(count + 1, 0)?;   // then fill
120    //     file.save_file_if_modified(false)?;                        // then write
121    //
122    // `false` matters: with `true` the engine puts a save dialog on screen, and
123    // a host with no operator would wait for an answer that never comes.
124    println!(
125        "
126  (this example does not write; enrolling a user would alter the station)"
127    );
128
129    Ok(())
130}

Trait Implementations§

Source§

impl Debug for UsersFile

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.