rafia_stpa/stpa/
uca_contexts.rs

1// *****************************************************************************
2// Copyright (c) 2025 Codethink
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0.
7//
8// This Source Code may also be made available under the following Secondary
9// Licenses when the conditions for such availability set forth in the Eclipse
10// Public License, v. 2.0 are satisfied: GNU General Public License, version 2
11// with the GNU Classpath Exception which is
12// available at https://www.gnu.org/software/classpath/license.html.
13//
14// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15// *****************************************************************************
16use std::{
17    ffi::OsStr,
18    fs::{self, File},
19    path::Path,
20};
21
22use csv;
23use serde::{Deserialize, Serialize};
24use serde_yaml;
25
26use crate::stpa::LoadError;
27
28#[derive(Debug, Serialize, Deserialize)]
29pub struct UcaContextCsv {
30    #[serde(rename = "Context Id")]
31    pub id: String,
32    #[serde(rename = "Unsafe Context")]
33    pub unsafe_context: String,
34    #[serde(rename = "Notes")]
35    pub notes: String,
36}
37
38#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
39pub struct UcaContext {
40    pub id: String,
41    pub unsafe_context: String,
42    pub notes: String,
43}
44
45impl UcaContext {
46    pub fn describe(&self) -> serde_json::Value {
47        serde_json::json!({"uca_context": self})
48    }
49}
50
51impl From<UcaContextCsv> for UcaContext {
52    fn from(csv: UcaContextCsv) -> Self {
53        UcaContext {
54            id: csv.id,
55            unsafe_context: csv.unsafe_context,
56            notes: csv.notes,
57        }
58    }
59}
60
61#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)]
62pub struct UcaContexts {
63    pub uca_contexts: Vec<UcaContext>,
64}
65
66impl UcaContexts {
67    pub fn from_yaml(text: &str) -> Result<UcaContexts, LoadError> {
68        serde_yaml::from_str::<UcaContexts>(text).map_err(LoadError::Yaml)
69    }
70    pub fn to_yaml(&self) -> Result<String, LoadError> {
71        serde_yaml::to_string(&self).map_err(LoadError::Yaml)
72    }
73    pub fn from_csv(filename: &Path) -> Result<UcaContexts, LoadError> {
74        let mut uca_contexts = vec![];
75        let file = File::open(filename)?;
76        for uca_context in csv::ReaderBuilder::new()
77            .from_reader(file)
78            .deserialize::<UcaContextCsv>()
79        {
80            uca_contexts.push(uca_context?.into());
81        }
82        Ok(UcaContexts { uca_contexts })
83    }
84    pub fn from_file(filename: &Path) -> Result<UcaContexts, LoadError> {
85        match filename.extension().and_then(OsStr::to_str) {
86            Some("csv") => Self::from_csv(filename),
87            Some("yml") | Some("yaml") => {
88                let text = fs::read_to_string(filename)?;
89                Self::from_yaml(&text)
90            }
91            _ => Err(LoadError::NotSupportedFormat),
92        }
93    }
94    pub fn find(&self, id: &str) -> Option<&UcaContext> {
95        self.uca_contexts.iter().find(|uca| uca.id == id)
96    }
97}