1use crate::logging::init::init_logging;
2use crate::logging::ledger::ensure_ledger_key_exists;
3use std::error::Error;
4use std::fs;
5use std::path::Path;
6
7use csv::Writer;
9use fake::{Dummy, Fake, Faker};
10use fake::faker::internet::en::SafeEmail;
11use fake::faker::name::en::{FirstName, LastName};
12use serde::Serialize;
13
14#[derive(Serialize, Dummy)]
18struct Customer {
19 id: i64,
20 #[dummy(faker = "FirstName()")]
22 first_name: String,
23 #[dummy(faker = "LastName()")]
24 last_name: String,
25 #[dummy(faker = "SafeEmail()")]
26 email: String,
27}
28
29fn create_sample_csv(path: &Path) -> Result<(), Box<dyn Error>> {
31 let mut wtr = Writer::from_path(path)?;
32
33 for i in 1..=30 {
34 let mut customer: Customer = Faker.fake();
36 customer.id = i;
37
38 wtr.serialize(customer)?;
39 }
40
41 wtr.flush()?;
42 Ok(())
43}
44
45pub fn init_project() -> Result<String, Box<dyn Error>> {
46 init_logging();
47 ensure_ledger_key_exists();
48
49 let mut actions = Vec::new();
50
51 let contracts_dir = Path::new("contracts");
53 fs::create_dir_all(contracts_dir)?;
54
55 let data_dir = Path::new("data");
56 if !data_dir.exists() {
57 fs::create_dir_all(data_dir)?;
58 actions.push("Created data directory");
59 }
60
61 if !Path::new(".env").exists() {
62 let env_content = r#"# .env file for project-specific environment variables
63
64S3_ACCESS_KEY=<access_key>
65S3_SECRET_KEY=<secret_key>
66
67#Currently this uses the key from the storage account. If you use in production remember to rotate the key based on your companies policy
68AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=<account_name>;AccountKey=<account_key>;EndpointSuffix=core.windows.net
69
70GCP_SERVICE_ACCOUNT_KEY='{"type": "service_account","project_id": <your_project_id>,"private_key_id": <your_private_key_id>,"private_key": "-----BEGIN PRIVATE KEY-----<The actual key from the file, this will be very long>-----END PRIVATE KEY-----\n","client_email": <user@project.iam.gserviceaccount.com>,"client_id": <your_client_id>,"auth_uri": "https://accounts.google.com/o/oauth2/auth","token_uri": "https://oauth2.googleapis.com/token","auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs","client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/<email>","universe_domain": "googleapis.com"}'
71"#;
72 fs::write(".env", env_content)?;
73 actions.push("Created .env file");
74 }
75
76 if !Path::new("profiles.toml").exists() {
77 let profile_content = r#"# Pipe Audit profiles.toml example
78[s3_test]
79provider = "s3"
80endpoint = "http://developyr.local:9000"
81region = "us-east-1"
82access_key = "${S3_ACCESS_KEY}"
83secret_key = "${S3_SECRET_KEY}"
84path_style = true
85use_ssl = false
86
87[azure_test]
88provider = "azure"
89connection_string = "${AZURE_STORAGE_CONNECTION_STRING}"
90
91[gcs_test]
92provider = "gcs"
93service_account_json = "${GCP_SERVICE_ACCOUNT_KEY}"
94"#;
95 fs::write("profiles.toml", profile_content)?;
96 actions.push("Created profiles.toml");
97 }
98
99 let contract_path = contracts_dir.join("example.toml");
101 if !contract_path.exists() {
102 let contract_content = r#"# contracts/example.toml
103[contract]
104name = "customers"
105version = "0.1.1"
106tags = ["pii", "critical"]
107
108[file]
109validation = [
110 { rule = "row_count", min = 0, max = 20}
111]
112
113[[columns]]
114name = "id"
115validation = [
116 { rule = "not_null" },
117 { rule = "unique" }
118]
119
120[[columns]]
121name = "email
122validation = [
123 { rule = "pattern", pattern = "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$" }
124]
125
126[source]
127type = "local"
128location = "data/customers.csv"
129profile = "local"
130
131"#;
132 fs::write(&contract_path, contract_content)?;
133 actions.push("Created contracts/example.toml");
134 }
135
136 let customers_csv_path = data_dir.join("customers.csv");
137 if !customers_csv_path.exists() {
138 create_sample_csv(&customers_csv_path)?;
139 actions.push("Created sample data/customers.csv");
140 }
141
142 if actions.is_empty() {
143 Ok("Project already initialized. No changes were made.".to_string())
144 } else {
145 Ok(format!(
146 "Successfully initialized project. Actions: {}",
147 actions.join(", ")
148 ))
149 }
150}