Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added feature synth-doc #99

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion synth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,4 @@ indicatif = "0.15.0"

dirs = "3.0.2"
uuid = { version = "0.8.2", features = ["v4"] }
mongodb = {version = "1.2.1", features = ["sync"] , default-features = false}
mongodb = {version = "1.2.1", features = ["sync"] , default-features = false}
4 changes: 4 additions & 0 deletions synth/src/cli/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use std::str::FromStr;
use crate::cli::mongo::MongoExportStrategy;
use synth_core::{Name, Namespace};

use super::synth_doc::DocExportStrategy;

pub trait ExportStrategy {
fn export(self, params: ExportParams) -> Result<()>;
}
Expand All @@ -23,6 +25,7 @@ pub enum SomeExportStrategy {
StdoutExportStrategy(StdoutExportStrategy),
FromPostgres(PostgresExportStrategy),
FromMongo(MongoExportStrategy),
DocExportStrategy(DocExportStrategy)
}

impl ExportStrategy for SomeExportStrategy {
Expand All @@ -31,6 +34,7 @@ impl ExportStrategy for SomeExportStrategy {
SomeExportStrategy::StdoutExportStrategy(ses) => ses.export(params),
SomeExportStrategy::FromPostgres(pes) => pes.export(params),
SomeExportStrategy::FromMongo(mes) => mes.export(params),
SomeExportStrategy::DocExportStrategy(doc) => doc.export(params),
}
}
}
Expand Down
32 changes: 32 additions & 0 deletions synth/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod postgres;
mod stdf;
mod store;
mod telemetry;
mod synth_doc;

use crate::cli::export::SomeExportStrategy;
use crate::cli::export::{ExportParams, ExportStrategy};
Expand All @@ -21,6 +22,8 @@ use crate::cli::telemetry::TelemetryClient;
use rand::RngCore;
use synth_core::Name;

use self::synth_doc::DocExportStrategy;

pub struct Cli {
store: Store,
args: CliArgs,
Expand Down Expand Up @@ -114,6 +117,11 @@ impl Cli {
}
}
}
CliArgs::Doc{ref namespace} => {
let to = SomeExportStrategy::DocExportStrategy(DocExportStrategy::new(namespace.clone()).context("could not open the namespace")?);
self.doc(namespace.clone(), to, 1)?;
Ok(())
}
}
}

Expand Down Expand Up @@ -235,6 +243,25 @@ impl Cli {
.export(params)
.context(format!("At namespace {:?}", ns_path))
}
fn doc(&self, ns_path: PathBuf, to: SomeExportStrategy, seed: u64) -> Result<()> {
if !self.workspace_initialised() {
return Err(anyhow!(
"Workspace has not been initialised. To initialise the workspace run `synth init`."
));
};
let namespace = self
.store
.get_ns(ns_path.clone())
.context("Unable to open the namespace")?;
let params = ExportParams {
namespace,
collection_name: None,
target: 2,
seed
};
to.export(params).context("Error generating doc")?;
Ok(())
}
}

#[derive(StructOpt)]
Expand Down Expand Up @@ -287,6 +314,11 @@ pub enum CliArgs {
)]
from: Option<SomeImportStrategy>,
},
#[structopt(about = "Write documentation for namespace")]
Doc{
#[structopt(about = "namespace to be documented", parse(from_os_str))]
namespace: PathBuf
},
#[structopt(about = "Toggle anonymous usage data collection")]
Telemetry(TelemetryCommand),
}
Expand Down
128 changes: 128 additions & 0 deletions synth/src/cli/synth_doc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
use synth_core::{Content, Name, Namespace, schema::{ArrayContent, ObjectContent}};
use std::{convert::TryFrom, fs::{File, OpenOptions}, io::{BufWriter, Write}, path::{Path, PathBuf}, str::FromStr};
use crate::sampler::Sampler;
use super::{ExportStrategy, ExportParams};
use anyhow::{Result, Context};

#[derive(Clone, Debug)]
pub struct DocExportStrategy{
file_path: PathBuf
}

impl ExportStrategy for DocExportStrategy{
fn export(self, params: ExportParams) -> Result<()> {
let generator = Sampler::try_from(&params.namespace)?;
let values = generator.sample_seeded(params.collection_name, params.target, params.seed)?;
let file = OpenOptions::new().create(true).read(true).append(true).open(self.file_path.clone())?;
let mut file = BufWriter::new(file);
write_doc(&params.namespace, &mut file)?;
//example result
writeln!(&mut file, "```json")?;
serde_json::to_writer_pretty(&mut file, &values)?;
writeln!(file, "")?;
writeln!(&mut file, "```")?;
writeln!(file, "> Created with [synth](https://github.com/getsynth/synth)")?;
Ok(())
}
}

impl DocExportStrategy {
pub fn new(path: PathBuf) -> Result<Self> {
let file_path = Path::join(&path, Path::new("README.md"));
Ok(
Self {
file_path
}
)
}
}

fn write_doc(ns: &Namespace, file: &mut BufWriter<File>) -> Result<()>{
write_colls(ns, file)?;
writeln!(file, "---")?;
let pairs = ns.iter();
for (n,c) in pairs {
write_coll_details((n,c), file)?;
writeln!(file, "---")?;
};
writeln!(file, "---")?;
writeln!(file, "### Example")?;
Ok(())
}

fn write_colls(ns: &Namespace, file: &mut BufWriter<File>) -> Result<()> {
writeln!(file, "```text\n### Description\n\n\n\n\n```")?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This "```text" makes the whole thing a code block. I don't think that's right here. Also below the Description header there should be a (please enter a descriptive text here) or some such.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I'll fix this. Thanks.

writeln!(file, "## Collections")?;
for key in ns.keys().map(|n| n.as_ref()) {
writeln!(file, "- [{}]({})", key, format!("#collection-{}", key)).context("error while writing to file")?;
};
Ok(())
}
fn write_coll_details((name, content ): (&Name,&Content), file: &mut BufWriter<File>) -> Result<()>{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually have spaces after commas. Perhaps cargo fmt the whole thing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure!

match content {
Content::Array(ArrayContent { content: box Content::Object(object_content), .. }) => {
let name_str = format!("Collection {}", name);
writeln!(file, "#### {}", name_str)?;
let name = Name::from_str("Collection")?;
write_coll_details((&name, &Content::Object(object_content.clone()) ), file)?;
write_foreigns((&name, &object_content), file)?;
}
Content::Object(obj) => {
writeln!(file, "##### Fields")?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5-level subheadings are clearly too far. We should remove one or two levels overall.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand. I should probably keep it at three

let ObjectContent { fields } = obj;
for (field_name, field_content) in fields.into_iter() {
let cont = &field_content.content;
let f_name = &Name::from_str(field_name)?;
if let box Content::Array(arr) = &cont {
write_subcoll(name, (f_name, arr), file)?;
} else {
write_coll_details((f_name, cont ), file)?;
}
}
},
Content::Null => {
writeln!(file, "##### {} [Type: *Null*]", name)?;
writeln!(file, "> Description goes here: ")?;
writeln!(file, "")?;
writeln!(file, "")?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to have multiple writeln! calls here:

Suggested change
writeln!(file, "##### {} [Type: *Null*]", name)?;
writeln!(file, "> Description goes here: ")?;
writeln!(file, "")?;
writeln!(file, "")?;
writeln!(file, "##### {} [Type: *Null*]\n> Description goes here: \n\n", name)?;

Copy link
Contributor Author

@OLUWAMUYIWA OLUWAMUYIWA Aug 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh.. No need? I'll do that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll write it all once as a single formatted string

}
Content::Bool(_) | Content::String(_) | Content::Number(_) | Content::SameAs(_)=> {
writeln!(file, "##### {} [Type: *{}*]", name, content.kind())?;
writeln!(file, "> Description goes here: ")?;
writeln!(file, "")?;
writeln!(file, "")?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

}
Content::OneOf(one_of) => {
writeln!(file, "##### {} [Type: *{}*]", name, content.kind())?;
writeln!(file, "> Description goes here: ")?;
writeln!(file, "**Variants:**")?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here.

for one in one_of.iter() {
write_coll_details((name, one ), file)?;
};
}
_ => {

}
};
Ok(())
}

fn write_foreigns((name, obj):(&Name, &ObjectContent), file: &mut BufWriter<File>) -> Result<()> {
writeln!(file, "#### Foreign Keys")?;
let ObjectContent { fields } = obj;
for (_, field_content) in fields.into_iter() {
if let box Content::SameAs(same) = &field_content.content {
let foreign_coll = same.ref_.collection().as_ref();
let foreign_feild = same.ref_.iter_fields().last().ok_or_else(|| anyhow::anyhow!("Could not get the last field"))?;
writeln!(file, "- {} refers to: **field** *{}* in **Collection** [{}](#collection-{}):", name, foreign_feild, foreign_coll, foreign_coll)?;
}
};
Ok(())
}

fn write_subcoll(parent: &Name, (name, arr):(&Name, &ArrayContent), file: &mut BufWriter<File>) -> Result<()> {
let name_str = format!("Subcollection {}: Parent: {}", name, parent);
writeln!(file, "{}", name_str)?;
write_coll_details((name, &Content::Array(arr.clone()) ), file)?;
Ok(())
}