-
Notifications
You must be signed in to change notification settings - Fork 25
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
Tree refactoring and basic text writing #90
Conversation
Created the TomlTextWriter interface to handle low-level writing of Toml files. One implementation writes to a StringBuilder, the other writes to an Okio BufferedSink. KtomlConf has also been amended to include an "indentation" property.
This interface roughly corresponds to the kotlinx-serialization.json library's Composer interface in function, so the name was changed to reflect that. This also clears up potential confusion with TomlWriter and TomlFileWriter, which will have a different purpose.
Classes deriving from TomlValue now expose their content property in an internal constructor, and parsing was moved to a secondary constructor. Since member functions are inaccessible before the super constructor is called, private functions used for parsing were moved to companion objects.
Hmm, not off to a good start with the workflows I'll fix the formatting |
Cleaned formatting and fixed style inconsistencies Diktat caught.
Alright, fixed. Sorry about that |
@NightEule5 that's fucking awesome! Thank you so much |
Thanks! :) Sure, no rush |
Nice work. :D A little suggestion, consider matching the type of content for each TomlValue. BTW, I didn't find any actual class for tables. I may try to implement inline ones first. Shall I go with this thread or open a new PR? Also, should I defer inline ones and not inline ones? They behave quite differently. 🤔 |
Hey! You can open a new PR, but I suppose that we will need to merge this huge PR first… |
I'm OK with that. I'll follow up when this is done. :D |
Thanks :)
Yep, I left it how it was for now. Constraining the type on an overridden var property gives an error, and I didn't try much yet to make that work |
Any chances that you will provide any tests that will cover this functionality? :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As it does not affect decoding logic - we can merge it, but let's discuss comments, that I have written. It also will be great if you will add more comments to the code and give a small overview on your plan :)
val escapedQuotesInLiteralStringsAllowed: Boolean = true | ||
) | ||
val escapedQuotesInLiteralStringsAllowed: Boolean = true, | ||
val indentation: Indentation = FOUR_SPACES |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please add a trailing comma here, so we will avoid 2-lines changes in the future?
@@ -24,6 +28,7 @@ public open class Toml( | |||
) : StringFormat { | |||
// parser is created once after the creation of the class, to reduce the number of created parsers for each toml | |||
public val tomlParser: TomlParser = TomlParser(config) | |||
public val tomlWriter: TomlWriter = TomlWriter(config) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please add some comment to this writer for newbies?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. Perhaps editing the comment above tomlParser
to apply to both the parser and the writer would suffice? Or do you mean description of its function?
/** | ||
* @property string | ||
*/ | ||
public enum class Indentation(public val string: String) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
may be call it value? string looks a little bit confusing, usually Java/Kotlin developers expect toString() or value
/** | ||
* Abstracts the specifics of writing TOML files into "emit" operations. | ||
*/ | ||
public interface TomlComposer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need an interface if it is inherited only in one abstract class now? Any future plans?
I suggest to merge it with AbstractEmmiter if it is not used anywhere else
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No specific plans for it, I just thought this way was best practice. I can merge it if that's preferable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is a best practice when it has common sense. But here we interface it "just because" 😄
Better to merge, so the project won't be too huge to read for new developers
/** | ||
* The based implementation of [TomlComposer]. | ||
*/ | ||
public abstract class AbstractTomlComposer(ktomlConf: KtomlConf) : TomlComposer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure that Composer
is a right name? I have seen Emitter
in projects like 'snakeyaml'. What do you think, does it sound better?
Or you are trying to make it similar to kotlinx.Json?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that was the idea. Emitter is probably a more fitting and clear name though, so I'll change it
public fun write(file: TomlFile, composer: TomlComposer): Unit = | ||
file.children.forEach { composer.writeChild(it) } | ||
|
||
private fun TomlComposer.writeChild(node: TomlNode) = when (node) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You plan to insert Toml nodes to the Tree here? What is your plan on this code?
You plan to take an object -> create tree -> then emit string or file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the plan here is to emit a tree that was created before to the Composer (Emitter), which then writes to a StringBuilder or BufferedSink depending on the implementation
protected abstract fun emit(fragment: Char) | ||
|
||
protected abstract fun emit(fragment1: String, fragment2: String) | ||
protected abstract fun emit( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the user scenario for that bunch of emit methods? Do we really need all of them? Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use case was for parts of the file that would need multiple emit calls at once, like strings (a quote, the value, another quote), comments, and indentation. In hindsight it'd be better to simplify this into two abstract emits, char and string
val ktomlPath = tomlFile.toPath() | ||
return getOsSpecificFileSystem().sink(ktomlPath).buffer() | ||
} catch (e: FileNotFoundException) { | ||
println("Not able to fine toml-file in the following path: $tomlFile") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fine
-> find
|
||
private fun TomlComposer.writeChild(node: TomlNode) = when (node) { | ||
is TomlFile -> | ||
error( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should have proper exception instead of error here...
val keys = key.keyParts | ||
|
||
if (keys.isEmpty()) { | ||
emitQuotedKey("") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keys can have a single quote also...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, that's when the key is empty
Yes, that's the plan :) I can do that in a later PR since this one got too large
Sure, I'll add more documentation My plan is to encode to the AST in the Encoder and CompositeEncoder implementation(s), then emit the AST via the appropriate Emitter. TomlWriter would traverse the tree, calling the necessary emit functions instead of writing the file or string directly. At some point we could make the AST |
For further development, comments in TOML file should be considered. I personally think annotation(such as
|
Yes, I think this is a good idea. We'll have to add comments to the AST later Other annotations would be good too, like |
could you please describe this Annotation logic here: #79 And let's merge this PR when you are ready. But please use Squash and merge. |
No description provided.