Skip to main content

Si

Struct Si 

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

SiServer API client with advanced session management.

This client handles the complex two-stage authentication flow required by SiServer and provides methods for report submission and calendar data retrieval. It implements resilient error handling to ensure application stability.

§Thread Safety

The client is not thread-safe due to mutable retry state. Each thread should use its own client instance for concurrent operations.

§Authentication Architecture

SiServer uses a sophisticated authentication system:

  1. LDAP Stage: Credentials sent to LDAP endpoint, token received
  2. Session Stage: Token sent to session endpoint, cookie received
  3. API Usage: Cookie included in all subsequent API requests

This design provides enhanced security but requires careful session management.

Implementations§

Source§

impl Si

Source

pub fn new(config: &SiConfig) -> Self

Creates a new SiServer API client instance.

Initializes the HTTP client with default settings suitable for SiServer API interactions. The client is configured for both JSON and multipart requests to handle different SiServer endpoints appropriately.

§Arguments
  • config - SiServer configuration containing API endpoints and user information
§Examples
use kasl::api::si::{Si, SiConfig};

let config = SiConfig {
    login: "username".to_string(),
    auth_url: "https://auth.company.com".to_string(),
    api_url: "https://api.company.com".to_string(),
};
let si = Si::new(&config);
Source

pub async fn send(&mut self, data: &str, date: &NaiveDate) -> Result<StatusCode>

Submits a daily time tracking report to SiServer.

This method sends formatted daily report data to the SiServer API for payroll and time tracking integration. It handles session management and implements retry logic for authentication failures.

§Report Format

The report data should be a JSON string containing:

  • Work hours and break information
  • Task completion details
  • Productivity metrics
  • Any relevant metadata for the specified date
§Session Management

The method implements automatic session handling:

  1. Session Retrieval: Get or create a valid session token
  2. Report Submission: Send report data with session authentication
  3. Error Handling: Detect expired sessions and retry with re-authentication
  4. Status Return: Return HTTP status for caller handling
§Arguments
  • data - JSON string containing the formatted report data
  • date - The date for which the report is being submitted
§Returns

Returns the HTTP status code from the API response, allowing callers to determine success or specific failure modes.

§Errors

Returns an error if:

  • Session management fails persistently
  • Network request fails
  • Request formatting fails
  • Duration conversion fails (internal error)
§Examples
let mut si = Si::new(&config);
let report_data = r#"{"hours": 8, "tasks": 5}"#.to_string();
let today = Local::now().date_naive();

let status = si.send(&report_data, &today).await?;
if status.is_success() {
    println!("Report submitted successfully");
}
Source

pub async fn send_monthly(&mut self, date: &NaiveDate) -> Result<StatusCode>

Submits a monthly summary report to SiServer.

Sends aggregated monthly statistics to the SiServer API for organizational reporting and payroll integration. The report covers the entire month containing the specified date.

§Monthly Report Contents

The system automatically generates a summary containing:

  • Total working hours for the month
  • Number of working days
  • Average daily productivity
  • Compliance with company policies
§Last Working Day Logic

Monthly reports are typically submitted on the last working day of each month. The system can automatically detect this condition and prompt for submission.

§Arguments
  • date - Any date within the target month for report generation
§Returns

Returns the HTTP status code from the API response.

§Examples
let mut si = Si::new(&config);
let today = Local::now().date_naive();

if si.is_last_working_day_of_month(&today)? {
    let status = si.send_monthly(&today).await?;
    if status.is_success() {
        println!("Monthly report submitted");
    }
}
Source

pub async fn rest_dates( &mut self, year: NaiveDate, ) -> Result<HashSet<NaiveDate>>

Fetches company rest dates and holidays for the specified year.

This method retrieves the official company calendar including holidays, vacation days, and extended weekend periods. The data is used for accurate productivity calculations and report generation.

§Error Resilience

This function prioritizes application stability over data completeness:

  • Network errors return empty results rather than failing
  • Authentication failures are logged but don’t interrupt operation
  • API parsing errors result in empty calendar (graceful degradation)
  • Session failures are handled with automatic retry

This design ensures that calendar integration enhances functionality without breaking core time tracking features when services are unavailable.

§Date Processing

The API returns three categories of rest dates:

  • Regular holidays (national and company holidays)
  • Vacation dates (company-specific rest periods)
  • Weekend extensions (long weekend periods)

All categories are combined into a single set for unified processing.

§Arguments
  • year - Any date within the target year for calendar retrieval
§Returns

Returns a HashSet<NaiveDate> containing all rest dates for the year. Returns an empty set on any error to ensure graceful degradation.

§Examples
let mut si = Si::new(&config);
let this_year = Local::now().date_naive();

let rest_dates = si.rest_dates(this_year).await?;
println!("Found {} rest dates this year", rest_dates.len());

// Check if a specific date is a rest day
let today = Local::now().date_naive();
if rest_dates.contains(&today) {
    println!("Today is a company rest day");
}
Source

pub fn is_last_working_day_of_month(&self, date: &NaiveDate) -> Result<bool>

Determines if the specified date is the last working day of its month.

This utility function calculates whether a given date represents the final working day in its month, which is useful for triggering monthly report submissions and other end-of-month processing.

§Algorithm

The calculation process:

  1. Find Month End: Determine the last calendar day of the month
  2. Weekend Adjustment: Move backward from weekends to find working days
  3. Comparison: Check if the input date matches the calculated last working day
§Limitations

Currently only considers weekends (Saturday/Sunday) as non-working days. Future versions may integrate with the rest dates API to consider holidays and company-specific non-working days for more accurate calculations.

§Arguments
  • date - The date to check against the last working day
§Returns

Returns true if the date is the last working day of its month, false otherwise.

§Errors

Currently cannot fail, but returns Result for consistency and future enhancement with holiday integration.

§Examples
let si = Si::new(&config);
let date = NaiveDate::from_ymd_opt(2024, 1, 31).unwrap(); // January 31st

if si.is_last_working_day_of_month(&date)? {
    println!("Time to submit monthly report!");
}

Trait Implementations§

Source§

impl Debug for Si

Source§

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

Formats the value using the given formatter. Read more
Source§

impl Session for Si

Source§

async fn login(&self) -> Result<String>

Performs two-stage authentication with SiServer.

This method implements SiServer’s unique authentication flow which requires two separate API calls to establish a session. The process is more complex than standard session authentication but provides enhanced security.

§Authentication Process
  1. LDAP Authentication: Send credentials to LDAP endpoint
  2. Token Extraction: Parse authentication token from response
  3. Session Exchange: Send token to session endpoint with Bearer auth
  4. Cookie Extraction: Parse session cookie from Set-Cookie header
  5. Format Preparation: Extract session ID for use in subsequent requests
§Error Scenarios
  • LDAP authentication failure (invalid credentials)
  • Token parsing failure (unexpected response format)
  • Session exchange failure (token expired or invalid)
  • Cookie extraction failure (missing or malformed Set-Cookie header)
§Returns

Returns the session ID string extracted from the PORTALSESSID cookie.

§Errors

Returns an error if:

  • No credentials have been set (programming error)
  • Network requests fail
  • LDAP authentication fails
  • Token or cookie parsing fails
  • Authentication flow completes but no valid session is established
Source§

fn set_credentials(&mut self, password: &str) -> Result<()>

Sets user credentials with SiServer-specific password encoding.

SiServer requires passwords to be double base64-encoded for security. This method handles the encoding and stores credentials in memory for use during the authentication process.

§Password Encoding

The password undergoes double base64 encoding:

  1. First encoding: base64(password)
  2. Second encoding: base64(base64(password))

This provides additional security layers for credential transmission.

§Arguments
  • password - The user’s SiServer password in plain text
§Returns

Always returns Ok(()) as encoding cannot fail.

Source§

fn session_id_file(&self) -> &str

Returns the filename for storing SiServer session tokens.

The session file is stored in the user’s application data directory and contains the cached session token for automatic login restoration.

Source§

fn secret(&self) -> Secret

Returns a configured Secret instance for secure password prompting.

The Secret manager handles secure password input with hidden characters and optional encrypted caching in the user’s data directory.

§Returns

A configured Secret instance with SiServer-specific prompts and file names.

Source§

fn retry(&self) -> i32

Returns the current authentication retry count.

Used by the session management system to track failed authentication attempts and implement retry limits.

Source§

fn inc_retry(&mut self)

Increments the authentication retry counter.

Called after each failed authentication attempt to track progress toward the maximum retry limit.

Source§

fn reset_retry(&mut self)

Resets the authentication retry counter to zero.

Called after successful authentication to ensure future session requests start with a clean slate.

Source§

async fn get_session_id(&mut self) -> Result<String>

Retrieves or establishes a valid session ID. Read more
Source§

fn read_session_id(file_name: &str) -> Result<String>

Reads a session ID from the specified file. Read more
Source§

fn write_session_id(file_name: &str, session_id: &str) -> Result<()>

Writes a session ID to the specified file. Read more
Source§

fn delete_session_id(&self) -> Result<()>

Deletes the cached session file. Read more

Auto Trait Implementations§

§

impl !RefUnwindSafe for Si

§

impl !UnwindSafe for Si

§

impl Freeze for Si

§

impl Send for Si

§

impl Sync for Si

§

impl Unpin for Si

§

impl UnsafeUnpin for Si

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more